How do I pass property values to custom control children without writing code?
Content page:
<views:ButtonView Image="newViolationSmall.png" Text="{Binding ParentsText}/>
Then ButtonView.xaml
<StackLayout
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SmartwebsCrossPlatform.Portable.Views.ButtonView">
<Button Image="{Binding Image}"/>
<Label Text="{Binding Text}" />
</StackLayout>
And ButtonView.cs
public partial class ButtonView : StackLayout {
...
//text
public static readonly BindableProperty TextProperty = BindableProperty.Create( "Text", typeof( string ), typeof( ButtonView ), null, BindingMode.Default, null, null, null, null );
public string Text {
get {
return (string)GetValue( TextProperty );
}
set {
SetValue( TextProperty, value );
}
}
//image
public static readonly BindableProperty ImageProperty = BindableProperty.Create( "Image", typeof( string ), typeof( ButtonView ) );
public string Image {
get {
return (string)GetValue( ImageProperty );
}
set {
SetValue( ImageProperty, value );
}
}
This does not show any text or image. A rather annoying alternative is to name both Button and Label, override ButtonView.OnPropertyChanged, and explicitly set their properties from code but it causes a System.ObjectDisposedException (Object name: 'Android.Graphics.Bitmap') when I navigate around this page.
#yyou's solution works for const values, but not for parent's properties.
The only thing missing is to set the BindingContext of this custom layout.
ie.
//ButtonView.xaml.cs
public ButtonView ()
{
InitializeComponent ();
BindingContext = this;
}
//to use this ButtonView component in a page
//ie. DetailPage.xaml
<ContentPage ...>
<app2:ButtonView Text="{Binding ParentText}" Image="{Binding ButtonImage }"/>
</ContentPage>
//in DetailPage.xaml.cs
public DetailPage() {
InitializeComponent ();
BindingContext = new DetailViewModel() {
ParentText = "sjldfdkfdjd",
ButtonImage = "my-button-image.png"
};
}
//in the file DetailViewModel.cs
namespace App2.ViewModels
{
public class DetailViewModel : BaseViewModel {
private string _parentText;
public string ParentText {
get { return _parentText; }
set { SetProperty<string>(ref _parentText, value); }
}
private string _buttonImage;
public string ButtonImage {
get { return _buttonImage; }
set { SetProperty<string>(ref _buttonImage, value); }
}
//other stuff
}
}
Related
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.
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.
Is there an alternative for a Tag Bindable Property?
A tag property is available in WPF. However, it does not exist in Xamarin.Forms.
I would like to create a bindable context between two elements.
Specifically, I would like to bind a label's Tag property to an entry's Text property.
I have attempted the following:
Tag Property
public class TagBehavior
{
public static readonly BindableProperty TagProperty = BindableProperty.CreateAttached<TagBehavior, BindableObject>(
bindableObject => TagBehavior.GetTag(bindableObject),
null, /* default value */
BindingMode.OneWay,
null,
(bo, old, #new) => TagBehavior.OnCompletedChanged(bo, old, #new),
null,
null);
public static BindableObject GetTag(BindableObject bindableObject)
{
return (BindableObject)bindableObject.GetValue(TagBehavior.TagProperty);
}
public static void SetTag(BindableObject bindingObject, BindableObject value)
{
bindingObject.SetValue(TagBehavior.TagProperty, value);
}
public static void OnCompletedChanged(BindableObject bindableObject, BindableObject oldValue, BindableObject newValue)
{
//var tag = TagBehavior.GetTag(entry);
//if (tag != null)
//{
// Debug.WriteLine("TODO - Handle tag's value change event");
//}
}
}
XAML
xmlns:Behaviors="clr-namespace:ViewMenu.Behaviors;assembly=ViewMenu"
. . .
<Entry x:Name="textBox1" BindingContext="{StaticResource ViewModel}" Text="{Binding Path=Content1}" Grid.Row="0" Grid.Column="0" >
<Entry.Behaviors>
<Behaviors:TextBoxFocusBehavior />
</Entry.Behaviors>
</Entry>
<Label x:Name="label1" Grid.Row="0" Grid.Column="0"
Behaviors:TagBehavior.Tag="{Binding Source={x:Reference textBox1}, Path=Text}">
<Label.Behaviors>
<Behaviors:LabelDisplayBehavior />
</Label.Behaviors>
</Label>
However, I get an error in the output window saying:
SetValue: Can not convert to type
'Xamarin.Forms.BindableObject'
Any suggestions?
You're creating an attached property of type BindableObject which you try to bind to the property Text of the TextBox which is of type string. Obviously, string cannot be casted to BindableObject hence the error.
Instead, declare your attached property of type string, or if you want to stay more general, of type object:
public class TagBehavior
{
public static readonly BindableProperty TagProperty = BindableProperty.CreateAttached<TagBehavior, string>(
bindableObject => TagBehavior.GetTag(bindableObject),
null, /* default value */
BindingMode.OneWay,
null,
(bo, old, #new) => TagBehavior.OnCompletedChanged(bo, old, #new),
null,
null);
public static string GetTag(BindableObject bindableObject)
{
return (string)bindableObject.GetValue(TagBehavior.TagProperty);
}
public static void SetTag(BindableObject bindingObject, string value)
{
bindingObject.SetValue(TagBehavior.TagProperty, value);
}
public static void OnCompletedChanged(BindableObject bindableObject, string oldValue, string newValue)
{
//var tag = TagBehavior.GetTag(entry);
//if (tag != null)
//{
// Debug.WriteLine("TODO - Handle tag's value change event");
//}
}
}
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}
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!