Windows store 8.1 dynamic binding not updating in UI - data-binding

I have made a simple test for updating binded values in the UI but nothing seems to update, only intial values are set but never updated, what would i be missing?
code:
//the model class
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged= delegate { };
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
public DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
Then simply in my main page i do this:
public ObservableCollection<DemoCustomer> progcollection = new ObservableCollection<DemoCustomer>();
public MainPage()
{
this.InitializeComponent();
progcollection = new ObservableCollection<DemoCustomer>();
this.progcollection.Add(new DemoCustomer());
this.txtblk.DataContext = progcollection[0].CustomerName;
}
Then in a click listener for example i do this:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
progcollection[0].CustomerName = "we changed the name!";
}
But nothing updates in the UI!!!
And here is my XAML:
<Page
x:Class="downloadprogressbinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:simpledownload"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="txtblk" HorizontalAlignment="Left" Margin="994,421,0,0" TextWrapping="Wrap" Text="{Binding Mode=TwoWay}" VerticalAlignment="Top" Height="89" Width="226" FontSize="36"/>
<Button Content="Button" HorizontalAlignment="Left" Height="51" Margin="116,24,0,0" VerticalAlignment="Top" Width="407" Click="Button_Click_1"/>
</Grid>

Using path keyword in binding and specifying the field solved it,like this:
{Binding Path=thetext, Mode=TwoWay}

Related

Xamarin ios share extension get image

I created demo application with Xamarin.Forms (5.X.X)
And i created share extension. Everything is good until here.
I want to get image from share extension but i cant get.
I tried almost everything.
public class CustomShareViewController: SLComposeServiceViewController
{
public CustomShareViewController(IntPtr handle) : base(handle)
{
}
public override void DidReceiveMemoryWarning()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
public override async void ViewDidLoad()
{
base.ViewDidLoad();
string urlstr = string.Empty;
NSExtensionItem item = ExtensionContext.InputItems[0];
var itemProvider = item.Attachments[0];
var type = itemProvider.Description.Split('"');
if (itemProvider.HasItemConformingTo(type[1]))
{
var load = await itemProvider.LoadItemAsync(type[1], null);
NSUrl newUrl = (NSUrl)load;
urlstr = newUrl.FilePathUrl.ToString();
}
if (!Xamarin.Forms.Forms.IsInitialized)
Xamarin.Forms.Forms.Init();
// Create an instance of XF page with associated View Model
var xfPage = new ShareMainPage();
var viewModel = (ShareMainPageViewModel)xfPage.BindingContext;
viewModel.Message = urlstr;
viewModel.Image = urlstr;
//viewModel.BindableImage = ImageSource.FromStream(() => new MemoryStream(byteArray));
// Override the behavior to complete the execution of the Extension when a user press the button
viewModel.DoCommand = new Command(() => DoneClicked());
// Convert XF page to a native UIViewController which can be consumed by the iOS Extension
var newController = xfPage.CreateViewController();
// Make sure the presentation style is set to full screen to avoid rendering the original entry point
newController.ModalPresentationStyle = UIModalPresentationStyle.FormSheet;
// Present new view controller as a regular view controller
this.PresentModalViewController(newController, false);
//var someController = this.Storyboard.InstantiateViewController("SomeController") as SomeViewController;
//NavigationController.PushViewController(someController, true);
//var itemProvider = inputItem?.Attachments?[0] as NSItemProvider;
//if (itemProvider.HasItemConformingTo("public.url"))
//{
// itemProvider.LoadPreviewImage(null, (item, error) => {
// if (item is UIImage)
// {
// var image = item as UIImage;
// var data = image.AsPNG();
// ImageView.Image = UIImage.LoadFromData(data);
// }
// });
//}
}
public override bool ShouldInteractWithUrl(UITextView textView, NSUrl url, NSRange characterRange, UITextItemInteraction interaction)
{
return base.ShouldInteractWithUrl(textView, url, characterRange, interaction);
}
public override bool IsContentValid()
{
// Do validation of contentText and/or NSExtensionContext attachments here
return true;
}
public override void DidSelectPost()
{
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
}
public override void PresentationAnimationDidFinish()
{
base.PresentationAnimationDidFinish();
}
private void DoneClicked()
{
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed-in items.
ExtensionContext.CompleteRequest(ExtensionContext.InputItems, null);
}
private void WriteToDebugFile(string dbgText)
{
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "Share-Extension-Debug.txt");
if (!File.Exists(filename))
{
File.WriteAllText(filename, $"\n{DateTime.Now} - {dbgText}");
}
else
{
File.AppendAllText(filename, $"\n{DateTime.Now} - {dbgText}");
}
}
}
I tried two ways.
Bind file path and get from there. ImageResource.FromFile(...) but it doesnt work.
Bind stream and it doesnt work too.
My sharepage.xml is
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Demo.ViewModels"
x:DataType="local:ShareMainPageViewModel"
xmlns:converters="clr-namespace:LifeApp.Mobile.Converters"
BackgroundColor="Green"
x:Class="LifeApp.Mobile.Views.ShareMainPage">
<ContentPage.BindingContext>
<local:ShareMainPageViewModel Message="Hello from Xamarin.Forms!" />
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Image Source="{Binding BindableImage}"
VerticalOptions="Start"
HorizontalOptions="CenterAndExpand" WidthRequest="200" HeightRequest="150"/>
<Label Text="{Binding Message}" TextColor="Black" FontSize="Medium" VerticalTextAlignment="Center"/>
<Button Command="{Binding DoCommand}" Text="Do the jungle!" TextColor="Black" />
</StackLayout>
</ContentPage.Content>
And SharePageViewModel
using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Forms.Shapes;
namespace Demo.ViewModels
{
public class ShareMainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _message;
public string Message
{
get { return _message; }
set
{
if (_message != value)
{
_message = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Message)));
}
}
}
private string _image;
public string Image
{
get { return _image; }
set
{
if (_image != value)
{
_image = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Image)));
}
}
}
public ImageSource _bindableImage { get; set; }
public ImageSource BindableImage
{
get
{
return _bindableImage;
}
set
{
if (_bindableImage !=null)
{
_bindableImage = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(BindableImage)));
}
}
}
private ICommand _doCommand;
public ICommand DoCommand
{
get { return _doCommand; }
set
{
if (_doCommand != value)
{
_doCommand = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(DoCommand)));
}
}
}
public ShareMainPageViewModel()
{
DoCommand = new Command(OnDoCommandExecuted);
}
private void OnDoCommandExecuted(object state)
{
// Acr.UserDialogs.UserDialogs.Instance.Alert("Completed");
Message = $"Job {Environment.TickCount} has been completed!";
}
}
}
I can get image file path but i cant bind and show image
file:///Users/xxxx/Library/Developer/CoreSimulator/Devices/58453DF3-58CF-4591-AC91-6A096FD7B814/data/Containers/Data/Application/1466F475-0F58-4F94-8511-B0BD9FE86DCA/tmp/WKFileShare-FbFp8eSD/adcf0796-2088-4d85-9420-c1d58c15cca4/....JPG
How can i solve this problem.

How send data (selectItem of listView) from xaml to ViewModel

I want get data selected in my viewModel
this is my xaml, but i donot know how solved my xaml, because this is bad, how use my behavior here in my xaml?
<ListView.Behaviors>
<behaviors:ItemTappedBehavior EventName="ItemSelected">
<behaviors:InvokeCommandAction Command="{Binding SelectedTagChanged}" />
</behaviors:ItemTappedBehavior>
</ListView.Behaviors>
<ListView ItemsSource="{Binding actors}" ItemTapped="ListView_ItemTapped">
in my behindcode
public partial class ActorsView : ContentPage
{
public AccountsView()
{
InitializeComponent();
}
async void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Actor selectedItem = (Actor)e.Item;
Console.WriteLine("WORK"+ Actor.Name);
}
but i want to get in my viewModel, dont in my behind code
i've seen to solved is with commands, or behaviors
this is my viewModel:
public class ActorsViewModel : ViewModelBase
{
public List<Actor> actors { get; set; }
public AccountsViewModel(IActorManager actorManager)
: base()
{
Edit, i am using commands but i dont know how use the answer from John Livermore, i want to use and show the console Console.WriteLine("ROW");.
public class ItemTappedBehavior : Behavior<ListView>
{
public ICommand Command { get; set; }
protected override void OnAttachedTo(ListView bindable)
{
base.OnAttachedTo(bindable);
}
protected override void OnDetachingFrom(ListView bindable)
{
base.OnDetachingFrom(bindable);
}
public Command SelectedTagChanged
{
get
{
return new Command(row =>
{
Console.WriteLine("ROW");
});
}
}
}
Since you had asked for a simpler Behavior for ListView selection changed. Please use the following:
Following class is inherited from BehaviorBase which takes care of BindingContext allocation.
On the OnAttachedTo override the ItemSelected event is hooked.
On the OnDetachingFrom override the ItemSelected event is unhooked.
Command bindable property Binds to the Command in ViewModel
In the event the e.SelectedItem is passed to the Command. (Change
it to whatever value you wish to pass)
public class SelectionChangedEventBehavior : BehaviorBase<ListView>
{
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(SelectionChangedEventBehavior), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
protected override void OnAttachedTo(BindableObject bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject.ItemSelected += AssociatedObject_ItemSelected;
}
protected override void OnDetachingFrom(ListView bindable)
{
base.OnDetachingFrom(bindable);
AssociatedObject.ItemSelected -= AssociatedObject_ItemSelected;
}
private void AssociatedObject_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (Command != null)
{
Command.Execute(e.SelectedItem);
}
}
}
Usage
Xaml
<ListView ItemsSource="{Binding Collection}">
<ListView.Behaviors>
<local:SelectionChangedEventBehavior Command="{Binding SelectionCommand}" />
</ListView.Behaviors>
</ListView>
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public Command<object> SelectionCommand { get; set; }
public ViewModel()
{
SelectionCommand = new Command<object>(OnSelection);
}
private void OnSelection(object item)
{
var selectedActor = (item as Actor);
}
}
Hope this could be useful for you. If possible use the EventToCommandBehavior as whole, just because you can reuse it for any control and any event.
In your XAML do the following:
Set your <ListView ItemsSource="{Binding actors}" ItemTapped="ListView_ItemTapped">
to be <ListView ItemsSource="{Binding actors}" SelectedItem = "{Binding SelectedActor, Mode=TwoWay}>
In your view model which your current XAML is bound to, create two new variables as so:
private Actor selectedActor;
public Actor SelectedActor {
get {return selectedActor;}
set {
selectedActor = value;
if(selectedActor != null) {//set properties here if needed}
OnPropertyChanged("SelectedActor"); //or however you raise your events, this is a
//custom method i've made
}
Now, where we define our actors item source, we will also define what a selectedActor is as so:
//get your actors, loop over them and add them to the list of actors
var actorItem = new Actor();
actors.Add(actorItem);
//outside of list raise the property changed event on your actors list.
OnPropertyChanged("actors")
That is a the pattern that i've used at work to get my selected item. If you then need to do something with it you could define it within the SelectedActor variable. For us our app is a hybrid between old xamarin (.Android and .iOS) and .Forms, so we pass Actions from the native level to the view model (ex: loading the summary page for the selected item)

UWP how to update a bound TextBox with changes in selected item of a bound ListView

I have a UWP Page, containing a form with textboxes and a ListView control. The ListView control is bound to a collection of Products. And I want that the bound textboxes should show the information regarding the product selected in the listview.
public class Product: INotifyPropertyChanged
{
public int ProductID { get; set; }
private string name;
public string Name {
get { return name; }
set
{
if (name==value)
return;
name = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public Product(int pid, string name)
{
ProductID = pid;
Name = name;
}
}
}
The XAML is as below:
<TextBox x:Name="txtProductId" Grid.Row="1" Grid.Column="1"
Text="{x:Bind CurrentProduct.ProductID}"/>
<TextBox x:Name="txtProductName" Grid.Row="2" Grid.Column="1"
Text="{x:Bind CurrentProduct.Name}" />
<ListView x:Name="lstProducts" Grid.Row="3" Grid.ColumnSpan="2"
ItemsSource="{x:Bind ProductList}"
SelectedItem="{x:Bind CurrentProduct, Mode=TwoWay}"
ItemTemplate="{StaticResource lstDataTemplate}"
>
</ListView>
The following code executes on Page_Loaded:
CurrentProduct = Products[0];
DataContext = CurrentProduct;
The ListView is bound to ProductList (Type ObservableCollection). I've noticed by executing the app in single step mode, the value of CurrentProduct is changing, but I think since it is the reference and not the property of the DataContext that changes, the PropertyChanged event is never fired and the TextBox doesn't get updated to show the name of the CurrentProduct.
I don't know how to proceed, any help would be appreciated.
The X:Bind default mode is OneTime, in your case, you need to set the mode to OneWay.
I made a code sample for your reference:
<TextBox x:Name="txtProductId" Grid.Row="1" Grid.Column="1"
Text="{x:Bind CurrentProduct.ProductID,Mode=OneWay}"/>
<TextBox x:Name="txtProductName" Grid.Row="2" Grid.Column="1"
Text="{x:Bind CurrentProduct.Name,Mode=OneWay}" />
<ListView x:Name="lstProducts" Grid.Row="3" Grid.ColumnSpan="2"
ItemsSource="{x:Bind ProductList}"
SelectedItem="{x:Bind CurrentProduct, Mode=TwoWay}"
>
</ListView>
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
ProductList.Add(new Product(i, "name " + i));
}
}
public ObservableCollection<Product> ProductList { get; set; } = new ObservableCollection<Product>();
private Product _CurrentProduct = new Product(100,"test");
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropertyName));
}
}
public Product CurrentProduct
{
get { return _CurrentProduct; }
set
{
if (_CurrentProduct != value)
{
_CurrentProduct = value;
RaisePropertyChanged("CurrentProduct");
}
}
}
}
public class Product : INotifyPropertyChanged
{
public int ProductID { get; set; }
private string name;
public string Name
{
get { return name; }
set
{
if (name == value)
return;
name = value;
RaisePropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public Product(int pid, string name)
{
ProductID = pid;
Name = name;
}
public override string ToString()
{
return Name;
}
}

xamarin design time binding cannot resolve property in data context

i'm currently in the process of modifying an ItemsView according to my needs. I noticed on flaw in my implementation however:
Unlike ListView i don't get intellisense according to my current iteration element. Does anyone know how to make that happen?
Here's my control implementation:
// http://adventuresinxamarinforms.com/2015/04/29/creating-a-xamarin-forms-accordion-control-without-custom-renders/
public class ItemsView : Grid
{
protected ScrollView ScrollView;
protected readonly StackLayout ItemsStackLayout;
public ItemsView()
{
ScrollView = new ScrollView();
ScrollView.SetBinding(ScrollOrientationProperty, new Binding(nameof(ScrollOrientation), mode: BindingMode.OneWay, source: this));
ItemsStackLayout = new StackLayout
{
Padding = new Thickness(0),
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand
};
ItemsStackLayout.SetBinding(StackOrientationProperty, new Binding(nameof(ItemsStackLayout), mode: BindingMode.OneWay, source: this));
ScrollView.Content = ItemsStackLayout;
Children.Add(ScrollView);
SelectedCommand = new Command<object>(item =>
{
var selectable = item as ISelectable;
if (selectable == null)
return;
SetSelected(selectable);
SelectedItem = selectable.IsSelected ? selectable : null;
});
}
protected virtual void SetSelected(ISelectable selectable)
{
selectable.IsSelected = true;
}
public bool ScrollToStartOnSelected { get; set; }
#region SelectedCommand
public static BindableProperty SelectedCommandProperty = BindableProperty.Create<ItemsView, ICommand>(d => d.SelectedCommand, default(ICommand));
public ICommand SelectedCommand
{
get { return (ICommand) GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
#endregion SelectedCommand
#region ScrollOrientation
public static BindableProperty ScrollOrientationProperty = BindableProperty.Create<ItemsView, ScrollOrientation>(d => d.ScrollOrientation, ScrollOrientation.Vertical);
public ScrollOrientation ScrollOrientation
{
get { return (ScrollOrientation) GetValue(ScrollOrientationProperty); }
set { SetValue(ScrollOrientationProperty, value); }
}
#endregion ScrollOrientation
#region StackOrientation
public static BindableProperty StackOrientationProperty = BindableProperty.Create<ItemsView, StackOrientation>(d => d.StackOrientation, StackOrientation.Vertical);
public StackOrientation StackOrientation
{
get { return (StackOrientation) GetValue(StackOrientationProperty); }
set { SetValue(StackOrientationProperty, value); }
}
#endregion StackOrientation
public event EventHandler SelectedItemChanged;
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create<ItemsView, IEnumerable>(p => p.ItemsSource, default(IEnumerable<object>), BindingMode.OneWay, null, ItemsSourceChanged);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty SelectedItemProperty =
BindableProperty.Create<ItemsView, object>(p => p.SelectedItem, default(object), BindingMode.TwoWay, null, OnSelectedItemChanged);
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create<ItemsView, DataTemplate>(p => p.ItemTemplate, default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
private static void ItemsSourceChanged(BindableObject bindable, IEnumerable oldValue, IEnumerable newValue)
{
var itemsLayout = (ItemsView)bindable;
itemsLayout.SetItems();
var newObservableCasted = newValue as INotifyCollectionChanged;
var oldObservableCasted = oldValue as INotifyCollectionChanged;
if (newObservableCasted != null)
newObservableCasted.CollectionChanged += itemsLayout.ItemsSourceCollectionChanged;
if (oldObservableCasted != null)
oldObservableCasted.CollectionChanged -= itemsLayout.ItemsSourceCollectionChanged;
}
private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
this.SetItems();
}
protected virtual void SetItems()
{
ItemsStackLayout.Children.Clear();
if (ItemsSource == null)
return;
foreach (var item in ItemsSource)
{
var itemView = GetItemView(item);
if (itemView == null)
{
ItemsStackLayout.Children.Add(new Label()
{
Text = "ItemTemplate missing."
});
break;
}
ItemsStackLayout.Children.Add(itemView);
}
SelectedItem = ItemsSource.OfType<ISelectable>().FirstOrDefault(x => x.IsSelected);
}
protected virtual View GetItemView(object item)
{
if (ItemTemplate == null)
return null;
ItemTemplate.SetValue(BindingContextProperty, item);
var content = ItemTemplate.CreateContent();
var view = content as View;
if (view == null)
return null;
var gesture = new TapGestureRecognizer
{
CommandParameter = item
};
gesture.SetBinding(TapGestureRecognizer.CommandProperty, (ItemsView v) => v.SelectedCommand, BindingMode.OneWay);
AddGesture(view, gesture);
return view;
}
protected void AddGesture(View view, TapGestureRecognizer gesture)
{
view.GestureRecognizers.Add(gesture);
var layout = view as Layout<View>;
if (layout == null)
return;
foreach (var child in layout.Children)
AddGesture(child, gesture);
}
private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
{
var itemsView = (ItemsView)bindable;
if (newValue == oldValue)
return;
var selectable = newValue as ISelectable;
itemsView.SetSelectedItem(selectable ?? oldValue as ISelectable);
}
protected virtual void SetSelectedItem(ISelectable selectedItem)
{
var items = ItemsSource;
foreach (var item in items.OfType<ISelectable>())
item.IsSelected = selectedItem != null && item == selectedItem && selectedItem.IsSelected;
var handler = SelectedItemChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
my viewmodels:
public class InfoFieldsViewModel : HeaderedViewModel
{
public override Task NavigatedToAsync(object state)
{
base.NavigatedToAsync(state);
var casted = state as SiteSelectionEntry;
if (casted != null)
{
HeaderText = $" < {casted.Name}";
}
Groups.IsEventNotificationEnabled = false;
var group = new InfoFieldGroupViewModel("Gruppe 1");
group.Items.Add(new InfoFieldDetailViewModel("Label1"));
group.Items.Add(new InfoFieldDetailViewModel("Label2"));
group.Items.Add(new InfoFieldDetailViewModel("Label3"));
Groups.Add(group);
group = new InfoFieldGroupViewModel("Gruppe 2");
group.Items.Add(new InfoFieldDetailViewModel("Label4"));
group.Items.Add(new InfoFieldDetailViewModel("Label5"));
group.Items.Add(new InfoFieldDetailViewModel("Label6"));
Groups.Add(group);
Groups.IsEventNotificationEnabled = true;
Groups.RaiseUpdate();
return Done;
}
private ExtendedObservableCollection<InfoFieldGroupViewModel> _groups = new ExtendedObservableCollection<InfoFieldGroupViewModel>();
public ExtendedObservableCollection<InfoFieldGroupViewModel> Groups
{
get { return GetValue(ref _groups); }
set { SetValue(ref _groups, value); }
}
}
public class EditableViewModel : ViewModelBase
{
private bool _isInEditMode = new bool();
public bool IsInEditMode
{
get { return GetValue(ref _isInEditMode); }
set { SetValue(ref _isInEditMode, value); }
}
}
public class InfoFieldGroupViewModel : ViewModelBase
{
public InfoFieldGroupViewModel()
{
IsExpanded = true;
}
public InfoFieldGroupViewModel(string groupName)
{
_groupName = groupName;
}
private bool _isExpanded = new bool();
public bool IsExpanded
{
get { return GetValue(ref _isExpanded); }
set { SetValue(ref _isExpanded, value); }
}
private string _groupName;
public string GroupName
{
get { return _groupName; }
set { SetValue(ref _groupName, value); }
}
private ExtendedObservableCollection<InfoFieldDetailViewModel> _items = new ExtendedObservableCollection<InfoFieldDetailViewModel>();
public ExtendedObservableCollection<InfoFieldDetailViewModel> Items
{
get { return GetValue(ref _items); }
set { SetValue(ref _items, value); }
}
}
public class InfoFieldDetailViewModel : EditableViewModel
{
public InfoFieldDetailViewModel()
{
}
public InfoFieldDetailViewModel(string label)
{
_label = label;
}
private string _label;
public string Label
{
get { return _label; }
set { SetValue(ref _label, value); }
}
}
The view which uses the controls:
<Grid BackgroundColor="{x:Static resources:Colors.DefaultBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<custom:ApplicationHeader
HeaderText="{Binding HeaderText}"
HeaderTapCommand="{Binding NavigatorBackCommand}"
HomeButtonCommand="{Binding NavigatorBackCommand}"/>
<Grid Row="1" custom:GridExtensions.IsBusy="{Binding IsBusy}">
<custom:ItemsView ItemsSource="{Binding Groups}">
<custom:ItemsView.ItemTemplate>
<DataTemplate>
<Label MinimumHeightRequest="30" Text="{Binding GroupName}"></Label>
</DataTemplate>
</custom:ItemsView.ItemTemplate>
</custom:ItemsView>
</Grid>
</Grid>
Screenshot of designtime error:
Oddly enough an ordinary xamarin.forms listview seems to have no trouble getting the design time correct here and mapping the child datacontext within the item template.
Is there some attribute i'm missing out on to make it work? Or am i doing something wrong in my implementation which makes this fail? The template itself renders just fine. So it's just the design time getting it wrong here.
Any ideas welcome. So far none of my binding context redirects worked successfully.
For me, it was a design-time DataType specification added on page level:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:**;assembly=**"
xmlns:bindingConverters="clr-namespace:**;assembly=**"
x:DataType="viewModels:WelcomeViewModel" <!-- HERE-->
x:Class="**.WelcomePage"
Title="{Binding Title}">
<ContentPage.Content >
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding Name}"/> <!-- this binding was looking for property 'Name' on root level, which is 'WelcomeViewModel' -->
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage.Content>
</ContentPage>
So, I've just removed x:DataType="viewModels:WelcomeViewModel" and it started working.

How to refresh data grid with new search results with MVVM Light

I'm using the latest MMVM Light windows 8 binaries and VS 2012 latest updates, so all is good there. I'm new to the MVVM Light framework, so it's an adjustment.
I have a Customers page with a grid that is searched with a textbox and button - the text box is bound and the button uses a command. The data is showing up in the view model just fine. I LINQ over the Customers List and set the Customers list property - all works well. The problem is, the page doesn't refresh. When I go to another page and return to the Customers page, the searched data is displayed.
I suspect the view model is static and needs to re-instantiated.
The follow are the respective code frags:
public partial class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
// Services
SimpleIoc.Default.Register<INavigationService, NavigationService>();
SimpleIoc.Default.Register<IMessenger, Messenger>();
// View Models
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<CustomersViewModel>();
SimpleIoc.Default.Register<CustomerViewModel>(true);
SimpleIoc.Default.Register<ContactsViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public CustomersViewModel Customers
{
get
{
return ServiceLocator.Current.GetInstance<CustomersViewModel>();
}
}
public CustomerViewModel Customer
{
get
{
return ServiceLocator.Current.GetInstance<CustomerViewModel>();
}
}
public ContactsViewModel Contacts
{
get
{
return ServiceLocator.Current.GetInstance<ContactsViewModel>();
}
}
public static void Cleanup()
{
}
}
}
public class CustomersViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private INavigationService _navigationService;
private IMessenger _messenger;
public RelayCommand<string> RefreshClickCommand { get; set; }
public RelayCommand<string> SearchCustomersCommand { get; set; }
public const string CustomersPropertyName = "Customers";
private ObservableCollection<Customer> _customers = null;
public ObservableCollection<Customer> Customers
{
get
{
return _customers;
}
set
{
if (_customers == value)
{
return;
}
_customers = value;
RaisePropertyChanging(CustomersPropertyName);
}
}
public const string WelcomeTitlePropertyName = "WelcomeTitle";
private string _welcomeTitle = string.Empty;
public string WelcomeTitle
{
get
{
return _welcomeTitle;
}
set
{
if (_welcomeTitle == value)
{
return;
}
_welcomeTitle = value;
RaisePropertyChanged(WelcomeTitlePropertyName);
}
}
public const string CustomerSearchTermPropertyName = "CustomerSearchTerm";
private string _customerSearchTerm = string.Empty;
public string CustomerSearchTerm
{
get
{
return _customerSearchTerm;
}
set
{
if (_customerSearchTerm == value)
{
return;
}
_customerSearchTerm = value;
RaisePropertyChanging(CustomerSearchTermPropertyName);
}
}
public Customer SelectedItem
{
set
{
Customer customer = value;
_messenger.Send<Customer>(customer, "Customer");
_navigationService.Navigate(typeof(CustomerPage));
}
}
public CustomersViewModel(IDataService dataService)
{
_navigationService = SimpleIoc.Default.GetInstance<INavigationService>();
_messenger = SimpleIoc.Default.GetInstance<IMessenger>();
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
if (error != null)
{
// Report error here
return;
}
WelcomeTitle = item.Title + "Customers";
});
GetCustomers();
InitializeCommands();
}
private void InitializeCommands()
{
RefreshClickCommand = new RelayCommand<string>((item) =>
{
GetCustomers();
});
SearchCustomersCommand = new RelayCommand<string>((item) =>
{
SearchCustomers();
});
}
private void GetCustomers()
{
_customers = _dataService.GetCustomers();
}
private void SearchCustomers()
{
var cust = _dataService.GetCustomers();
List<Customer> customers = (from c in cust
where c.CompanyName.StartsWith(_customerSearchTerm)
orderby c.CompanyName
select c).ToList();
_customers = new ObservableCollection<Customer>(customers);
}
}
<common:LayoutAwarePage x:Class="SalesAccountManager.Views.RelationshipManager.CustomersPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:common="using:SalesAccountManager.Common"
xmlns:ignore="http://www.ignore.com"
xmlns:telerikGrid="using:Telerik.UI.Xaml.Controls.Grid"
xmlns:WinRtBehaviors="using:WinRtBehaviors"
xmlns:Win8nl_Behavior="using:Win8nl.Behaviors"
mc:Ignorable="d ignore"
d:DesignHeight="768"
d:DesignWidth="1366"
DataContext="{Binding Customers, Source={StaticResource Locator}}">
....
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Text="Customers" FontFamily="Segoe UI" FontSize="38"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0, 0, 100, 0">
<TextBox Height="20" Width="600" Background="White" Text="{Binding CustomerSearchTerm, Mode=TwoWay}" />
<Button Background="White" Command="{Binding SearchCustomersCommand}">
<Image Source="../../Images/Search.jpg" Height="20" Width="20"></Image>
</Button>
</StackPanel>
</Grid>
Any guidance on this would be appreciated...
Thanks!

Resources