Data binding property not working - windows-phone-7

first of all sorry for my english.
I am developing a WP7 app, and I still haven't completely understood the data binding structure. I have a page that has some data obtained through data binding. Data is generated within the .cs, and it works fine.
But on another page I have some data that is obtained from data binding too, but I want it to come from a UI input text instead. It's simple, just a textbox and a textblock, so the user writes something on the textbox and so it shows up on the textblock that's on the same page. But it's not working, the textblock remains empty.
It's something like this:
<TextBox Name="TestInput">
<TextBlock Text="{Binding TestText}">
Above is what's on the XAML.
public partial class NewItem : PhoneApplicationPage
{
public String TestText { get; set; }
public NewItem()
{
InitializeComponent();
TestText = "TestInput.Text";
}
}
And this above is what's on the C#.
BUT!! It doesn't end here. Since the textblock wasn't showing anything, I ended desperately trying to assign some plain String to the TestText property. Like this:
TestText = "HELLO WORLD";
But when the app starts and the page loads, the textblock shows nothing. I just don't understand what am I missing, or doing wrong.
I will appreciate if someone could clear me up the databinding structure, or at least explain me what did I do wrong so I can figure it out myself.
Thanks in advance guys!

You have to assign the DataContext before you want the binding effect.So whenever there is a change in the text box, you write the code in the textchanged event.
this.DataContext=TestText
And one more change that you need to perform is that you are not actually setting the property.It should be like
TestText=TestInput.Text

for your understanding of binding i am putting a simple working example..just follow this..
this is you page on which you bind the data of textbox to someproperty textboxText..when you finished writing in this textbox.then all the writtentext automatically come ino this property. and this property is also binded to textbloack so when your textbloack got focus it will got to the get of the property and automatically fill it.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" >
<TextBox x:Name="testTextbox" Height="50" Width="200" Text="{Binding TextboxText,Mode=TwoWay}" />
<TextBlock x:Name="testTextblock" Height="50" Width="1000" Text="{Binding TextboxText,Mode=OneWay}" Foreground="White" />
</StackPanel>
</Grid>
this is your page.cs class in which i have also shown you how to implement inotifyproperty changed..it will help you in future..
public sealed partial class MainPage : Page,INotifyPropertyChanged
{
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private string _TextboxText;
public string TextboxText
{
get
{
return _TextboxText;
}
set
{
_TextboxText = value;
FirePropertyChanged("TextboxText");
testTextblock.UpdateLayout();
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Related

Telerik UWP DataForm changing bound data and Updating

I'm currently using telerik in UWP to create list of items, i want to be able to use a browse button and update a certain piece of data in the Telerik-RadDataForm. I have all the bindings setup using MVVM and it displays data fine if it isn't edited on the code side. My XAML is setup as so:
<Data:RadDataForm x:Name="dataform"
HorizontalAlignment="Left" Grid.Row="0"
Grid.RowSpan="2" Grid.Column="2"
VerticalAlignment="Center" Width="454"
Item="{Binding CurrentSceneViewModel, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" CommitMode="Immediate"
ValidationMode="Immediate" Height="664" Margin="0,28" />
The CurrentSceneViewModel is:
public SceneViewModel CurrentSceneViewModel
{
get => _currentSceneViewModel;
set=> _currentSceneViewModel= value;
}
And the data i wish to change is :
public string FileName
{
get => _fileName;
set
{
Scene.SceneFile = value;
_fileName = Path.GetFileName(value);
OnPropertyChanged(nameof(FileName));
}
}
The problem i have is pushing this information to the user interface the code-behind doesn't seem to update the UI, even using PropertyChanged. I'm not sure what else to try ? And if this is something the RadDataform simply doesn't support. It should be noted FileName is a property of CurrentScene ViewModel.
public abstract class BaseViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected IPageNavigationService navservice = new PageNavigationService();
}

Xamarin: Updating an ObservableCollection in the Background

I have a Xamarin.Forms Application based on a Listview populated by an ObservableCollection<Item> and bound to a SQLite model exposed as List<Item>.
Now I have difficulties figuring out how I can update the data via the web.
The whole process is supposed to run in the background. If everything runs as desired, the update process would be based on async ... await tasks and new items would appear one by one on the screen as they are pulled in.
Would anyone please guide me how to lay out my application and how to implement such a background update task?
Notes:
More experienced colleagues warned me that such a concept cannot be done in Xamarin at all, since, so they say, ObservableCollection "does not support to be updated by a background task". I did some research on this, and found indeed some indication that this could be true, but the infos were long outdated (from 2008), things have very likely changed since then.
For performance reasons I cannot simply pull in the complete list and throw away the existing list, but I need to implement a record based update looking at the items one by one. To accomplish this records have an unique Id, and are timestamped. My app knows when it has last seen the web service, and can query all items which have changed since then. I already have a REST service pulling in the changed Items data from the backend as a List, but cannot figure out how to refresh the ObservableCollection and the underlying database.
I do all my updates on change to the ListView. Here I have a button in the a list view which when clicked updates a property which persists by saving it to the sql database. It assumes you have your database set up.
Database:
Function which updates item if exist or saves new. This is a Task so can be called asynchronously.
public Task<int> SaveItemAsync(Item item)
{
if(item.ItemId != 0)
{
return database.UpdateAsync(item);
}
else
{
return database.InsertAsync(itme);
}
}
Xaml
List View which binds to an Observable collection created from the item database.
GestureRecognizers is set up on the image and is bound to a tapCommand in the ViewModel - The code behind the Xaml defines the binding context
Code behind
ItemViewModel viewModel;
public MessagePage ()
{
InitializeComponent ();
viewModel = new ItemViewModel();
BindingContext = viewModel;
}
And then the Xaml
Bind to the ObsevableCollection "ItemsPassed" and this set as the binding context within it. As a result you need to go back to the
page BindingContext so note the binding path for the TapCommand.
Pass the ItemId through as a parameter
<ListView ItemsSource="{Binding ItemsPassed}"
HasUnevenRows="True"
x:Name="ItemListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView>
<StackLayout Padding="20">
<StackLayout Orientation="Horizontal">
<StackLayout Orientation="Vertical" HorizontalOptions="Start">
<Label Text="{Binding ItemText}"
FontAttributes="Bold"/>
</StackLayout>
<Image Source="{Binding Favourited, HeightRequest="12" HorizontalOptions="EndAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference ItemListView}}"
CommandParameter="{Binding ItemId}"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel
Define the ICommand and assign a function to it
Find the item in the observable collection and change the property
This also needs to be changed in the database using the await App.Database.SaveItemAsync(item) - because of this the function called of the command must be async
public class ItemsViewModel : INotifyPropertyChanged
{
public ObservableCollection<Item> ItemsPassed { get; set; } = new ObservableCollection<Item>();
// Get items out of yourdatabase
public async void GetItems()
{
List<Item> itemsOut = await App.Database.GetItemsAsync();
foreach (Item i in itemsOut)
{
ItemsPassed.Add(i);
}
}
ICommand tapCommand;
public ItemsViewModel()
{
GetItems();
tapCommand = new Command(ExecuteTap);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
// Find the item selected and change the property
private async void ExecuteTap(object obj)
{
var item = ItemsPassed.FirstOrDefault(i => i.ItemId.ToString() == obj.ToString());
if (item != null)
{
if (item.Favourited == true)
{
item.Favourited = false;
}
else
{
item.Favourited = true;
}
await App.Database.SaveItemAsync(item);
Console.WriteLine("update the database");
}
}
}
You then want to make sure the changes occur in the view - this is done through extending INotifyPropertyChange in the Model and calling it when the property changes.
Model
When the item is changed in the viewmodel the OnPropertyChanged is fired which cause the view to update.
public class Item : INotifyPropertyChanged
{
[PrimaryKey, AutoIncrement]
public int ItemId { get; set; }
public string ItemText { get; set; }
public string Author { get; set; }
private bool _favourited;
public bool Favourited
{
get { return _favourited; }
set
{
_favourited = value;
OnPropertyChanged("Favourited");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Hope this helps. Sorry I had to put things in snippets the code section wasn't working properly probably because some of the code is wrong, but its just for example.

binding property of one field to another in wp7

<TextBlock Name="currency" TextWrapping="Wrap" VerticalAlignment="Center"/>
<TextBlock Margin="5,0" Text="{Binding Text, ElementName=currency" TextWrapping="Wrap" VerticalAlignment="Center" FontSize="22" />
I am using the above code for binding property of one field to another in my WP7 application.
i want to do the similar task from back-end. any suggestions??
Bindings are working in a specified data context. You can set the data context of your layout root to the page instance, then you can bind to any of your properties. (DataContext is inherited through the child FrameworkElements.) If you want your binding to update its value whenever you change your property from code, you need to implement INotifyPropertyChanged interface or use Dependency properties.
<Grid x:Name="LayoutRoot">
<TextBox Text="{Binding Test, Mode=TwoWay}" />
</Grid>
public class MainPage : PhoneApplicationPage, INotifyPropertyChanged
{
private string test;
public string Test
{
get { return this.test; }
set
{
this.test = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Test"));
}
}
public MainPage()
{
InitializeComponents();
LayoutRoot.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
}
This is a stupid example since you can access your TextBox any time from MainPage, this has much more sense if you are displaying model objects with DataTemplates.
(I typed this on phone, hope it compiles..)
i got my solution as: var b = new Binding{ElementName = "currency", Path = new PropertyPath("Text")}; Textblock txt = new TextBlock(); txt.SetBinding(TextBlock.TextProperty, b);

ListPicker does not 'tick' selected items when ItemsSource is binded to data fetched from service

I followed instructions at http://mobileworld.appamundi.com/blogs/andywigley/archive/2012/02/02/how-to-databind-selecteditems-of-the-listpicker-and-recurringdayspicker.aspx to bind SelectedItems in multiselect ListPicker.
When I bind ItemsSource to the ObservableCollection of objects created in the ViewModel, ListPicker works without any problems. However, when I bind ItemsSource to ObservableCollection fetched from WCF service, problems start. ListPicker displays all items properly, they can also be selected and displayed as selected on main screen. But when I click the picker to select again, ListPicker is not able to 'tick' selected items on the template.
I could rewrite the list of all elements fetched from service but I'm wondering if there is a neat way of solving that problem?
Project info: WP 7.0, WCF services, EntityFramework, lots of coffee
Entity class:
DataContract(IsReference=true)]
public class TypeOfDish
{
[DataMember]
public int TypeOfDishID { get; set; }
[DataMember]
public string NameToDisplay { get; set; }
}
WCF Service:
[ServiceContract]
public interface IMyService
{
[OperationContract]
[ApplyDataContractResolver]
IEnumerable<TypeOfDish> GetDishTypes();
}
Xaml:
<StackPanel x:Name="DishTypeGroup" Orientation="Vertical" Width="400">
<helpers:ListPickerExtended x:Name="TypeOfDishPicker" >
ItemsSource="{Binding DishTypeList}"
SelectionMode="Multiple"
SelectedItems="{Binding SelectedDishTypes, Mode=TwoWay}"
Margin="8,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=NameToDisplay}" />
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</helpers:ListPickerExtended>
</StackPanel>
ListPickerExtended.cs
public class ListPickerExtended : ListPicker
{
public new IList SelectedItems
{
get
{
return (IList)GetValue(SelectedItemsProperty);
}
set
{
base.SetValue(SelectedItemsProperty, value);
}
}
}
ViewModel
public ObservableCollection<TypeOfDish> DishTypeList
{
get
{
//myModel.DichTypes is loaded with data from calling
//GetDishTypesAsync() on my service client
return myModel.DishTypes;
}
}
ObservableCollection<object> _selectedDishes = new ObservableCollection<object>();
public ObservableCollection<object> SelectedDishTypes
{
get { return _selectedDishes; }
set
{
if (_selectedDishes == value) { return; }
_selectedDishes = value;
RaisePropertyChanged("SelectedDishTypes");
}
}
I've just found solution.
I was fetching data from the service every time the page was loaded. When returning from full mode template in ListPicker, the main page was reloaded, resetting DishTypesList property using newly fetched data. Although ListPicker.SelectedItems were still set, the new object were not matching the objects in them.
I just needed to move loading data into View Model constructor and ListPicker started working correctly.

What is the proper way to perform page navigation on ListBox selection changes

I'm trying the MVVM Light Toolkit. Though I still think having multiple ViewModels for such small apps is overkill, I like the concepts. What I still can't quite understand is how (or I should say "what is the recommended way") to navigate from one page to another when the selection changes in a ListBox.
The big problem with this toolkit is that it forces you to learn MVVM via other sources before using it, rather than show you what (its vision of) MVVM is from within the framework, accompanying samples and documentation. Are there samples out there showing the different concepts? And please, no videos.
Have you tried modifying your ListBox ItemTemplate to have each item be a HyperlinkButton and just setting the NavigateURI attribute to the Page you want to navigate to?
I still have not figured out how to do this (navigate to a details page upon selection changed in a listbox) without any codebehind in the view. However, if you are OK with having just a little codebehind in the view here's what I recommend:
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}"
SelectionChanged="MainListBox_SelectionChanged"
SelectedItem="{Binding Path=SelectedListItem, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
First, per above bind to the SelectedItem property of the Listbox with a TwoWay binding to a property in your ViewModel (SelectedListItem in the above).
Then in your codebehind for this page implement the handler for MainListBox_SelectionChanged:
// Handle selection changed on ListBox
private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// If selected index is -1 (no selection) do nothing
if (MainListBox.SelectedIndex == -1)
return;
// Navigate to the new page
NavigationService.Navigate(new Uri("/DetailsPage.xaml", UriKind.Relative));
}
This is the only codebehind you need in your main view.
In your main ViewModel you need a SelectedListItem property:
public const string SelectedListItemPropertyName = "SelectedListItem";
private ItemViewModel _SelectedListItem;
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding
/// </summary>
/// <returns></returns>
public ItemViewModel SelectedListItem
{
get
{
return _SelectedListItem;
}
set
{
if (value != _SelectedListItem)
{
_SelectedListItem = value;
RaisePropertyChanged(SelectedListItemPropertyName);
}
}
}
Now, the trick to getting the context passed to your details page (the context being what list item was selected) you need to setup the DataContext in your Details view:
public DetailsPage()
{
InitializeComponent();
if (DataContext == null)
DataContext = App.ViewModel.SelectedListItem;
}
Hope this helps.
eventually you'll want to do more than just navigate, potentially navigate after setting a custom object.
Here is a MVVM-light way of doing this.
You'll first want to bind your listbox selected item to a property in your viewmodel
<ListBox ItemsSource="{Binding Events}" Margin="0,0,-12,0" SelectedItem="{Binding SelectedEvent, Mode=TwoWay}">
Declare your SelectedEvent property
public const string SelectedEventPropertyName = "SelectedEvent";
private Event _selectedEvent;
public Event SelectedEvent
{
get {return _selectedEvent;}
set
{
if (_selectedEvent == value)
{
return;
}
var oldValue = _selectedEvent;
_selectedEvent = value;
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
RaisePropertyChanged(SelectedEventPropertyName, oldValue, value, true);
}
}
You can then define an interaction trigger bound to the tap event
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding EventPageCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
In your viewmodel, define your EventPageCommand as a RelayCommand:
public RelayCommand EventPageCommand { get; private set; }
public MainViewModel()
{
EventPageCommand = new RelayCommand(GoToEventPage);
}
and finally declare your GoToEventPage method
private void GoToEventPage()
{
_navigationService.NavigateTo(new Uri("/EventPage.xaml", UriKind.Relative));
}
note that you can do other actions before navigating to your new page, plus your selected item from your list box is currently set in the property you bound it too.

Resources