I believe this is possible, but I don't seem to be able to get it to work; here's my view model:
namespace MyApp.ViewModel
{
public class MainViewModel : INotifyPropertyChanged
{
private static MainViewModel _mvm;
public static MainViewModel MVM()
{
if (_mvm == null)
_mvm = new MainViewModel();
return _mvm;
}
private string _imagePath = #"c:\location\image.png";
public string ImagePath
{
get { return _imagePath; }
set
{
SetProperty<string>(ref _imagePath, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged<T>(propertyName);
return true;
}
private void OnPropertyChanged<T>([CallerMemberName]string caller = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(caller));
}
}
...
Here's my App.xaml:
xmlns:vm="using:MyApp.ViewModel">
<Application.Resources>
<ResourceDictionary>
<vm:MainViewModel x:Key="MainViewModel" />
</ResourceDictionary>
</Application.Resources>
Here's the binding:
<Page
...
DataContext="{Binding MVM, Source={StaticResource MainViewModel}}">
<StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
<TextBlock FontSize="30" Margin="10">Image</TextBlock>
<TextBox Text="{Binding ImagePath}" Margin="10"/>
</StackPanel>
...
I don't seem to be able to get the binding to work; what have I missed here? I would expect the field to be populated with the default value, but it isn't; I've put breakpoints in the ViewModel, but it is not breaking.
To me your binding syntax is incorrect. DataContext="{Binding MVM, Source={StaticResource MainViewModel} means you should have a "MVM" PROPERTY in your MainViewModel class. In your case MVM is a method.
Try replacing your MVM method by a property. That might work.
Another way to do it, is to set
DataContext="{StaticResource MainViewModel}"
In that case, the MVM method will be obsolete (I did not try it on WinRT)
Related
I have an entry control in my XAML where I set the initial value on page appear through data binding. Initially the value is appearing but when I am updating it from another view model it is not getting updated on UI.
Below is the XAML code and XAML.CS
<ListView
x:Name="workList"
Grid.Row="2"
SeparatorColor="{DynamicResource AccentColor}"
ItemsSource="{ Binding WorkItems }"
Margin="5"
CachingStrategy="RecycleElement"
RowHeight="440"
SeparatorVisibility="Default"
SelectionMode="None"
HasUnevenRows="False">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<local:LoadItemPutawayTemplate />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
mc:Ignorable="d"
x:Class="Sanipex.LoadItemPutawayTemplate">
<Grid
RowSpacing="0"
Padding="0"
Margin="0,10,0,0"
>
<Grid.RowDefinitions>
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Entry
x:Name="OverrideLoc"
Grid.Row="0"
TextColor="Black"
WidthRequest="110"
Text="{Binding toLocation}"
grial:EntryProperties.BorderCornerRadius="10"
grial:EntryProperties.BorderStyle="RoundRect"
grial:EntryProperties.BorderColor="Black"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
Focused="OverrideLoc_Focused"
TextChanged="OverrideLoc_TextChanged"
grial:EntryProperties.HorizontalPadding="5"
FontAttributes="Bold"
PlaceholderColor="Black"
FontSize="20"/>
</Grid>
public partial class ItemPutAway : ContentPage
{
private static ItemPutAwayViewModel obj;
public ItemPutAway()
{
InitializeComponent();
obj = new ItemPutAwayViewModel();
BindingContext = obj;
}
public static ItemPutAwayViewModel itemPutAwayViewModel
{
get
{
return obj;
}
}
protected override async void OnAppearing()
{
obj.LoadData();
}
}
Below is my first view model code
public class ItemPutAwayViewModel : INotifyPropertyChanged
{
private IList<WorkItem> workItems;
public event PropertyChangedEventHandler PropertyChanged;
public string ltoLocation;
public string toLocation
{
get => ltoLocation;
set
{
ltoLocation = value;
OnPropertyChanged(nameof(toLocation));
}
}
public IList<WorkItem> WorkItems
{
get => workItems;
set
{
workItems = value;
OnPropertyChanged(nameof(WorkItems));
}
}
public void LoadData()
{
WorkItems = App.dataManager.GetItemPutAwayWorks();
}
public void setLocation(string _location)
{
toLocation = _location;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Below is the code through which I am trying to update the toLocation binding value to different value from another XAML page as below:
public partial class AvailableLocationsPopUp : PopupPage
{
private static AvailableLocationViewModel obj;
public AvailableLocationsPopUp(WorkItem _workItem)
{
InitializeComponent();
obj = new AvailableLocationViewModel(gWorkItem.itemid);
BindingContext = obj;
}
private void OnClose(object sender, EventArgs e)
{
PopupNavigation.Instance.PopAsync();
}
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Location content = e.Item as Location;
ItemPutAway.itemPutAwayViewModel.setLocation("ABC-XYZ");
PopupNavigation.Instance.PopAsync();
}
}
As I mentioned in the discussion, you have to also implement the INotifyPropertyChanged interface of the class WorkItem.
Implement INotifyPropertyChanged in ItemPutAwayViewModel will only help for changes in the WorkItems(like add or remove one WorkItem), not the changes inside the WorkItem.
So, the code should be:
public class WorkItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _toLocation;
public string toLocation
{
get => _toLocation;
set
{
_toLocation = value;
NotifyPropertyChanged();
}
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
So I'm trying to implement a ListBoxItem that has a TextBlock that binds to a property in the viewmodel. But for some reason the view isn't being updated even after the viewmodel property is updated. Can anyone give me some pointers?
In the view (xaml)
<phone:Pivot HorizontalAlignment="Left" Height="748" Margin="10,10,0,0" Grid.Row="1" Title="pivot" VerticalAlignment="Top" Width="460">
<StackPanel x:Name="POIStackPanel" DataContext="{StaticResource POIViewModel}">
<ListBoxItem x:Name="SelectedPOIItem">
<TextBlock Text="{Binding SelectedPickupPOI.label}" />
</ListBoxItem>
phone:PivotItem x:Name="GooglePivot" Header="Google">
<ListBox x:Name="GooglePOIList"
ItemsSource="{Binding GooglePOICollection}"
HorizontalAlignment="Left"
Height="441"
Width="436"
SelectedItem="{Binding SelectedPickupPOI, Mode=TwoWay}"
ItemTemplate="{StaticResource POIItemTemplate}" />
</phone:PivotItem>
</StackPanel>
</phone:Pivot>
In the viewmodel
public ObservableCollection<PointOfInterest> GooglePOICollection { get; private set; }
public PointOfInterest SelectedPickupPOI
{
get
{
return _selectedPickupPOI;
}
set
{
_selectedPickupPOI = value;
NotifyPropertyChanged("SelectedPickupPOI");
}
}
public PointOfInterestViewModel()
{
this.GooglePOICollection = = new ObservableCollection<PointOfInterest>();
this.SelectedPickupPOI = new PointOfInterest();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
My PointOfInterest Models
public abstract class PointOfInterest : INotifyPropertyChanged
{
public abstract string id { get; set; }
public abstract string label { get; set; }
}
public class GooglePOI : PointOfInterest
{
private string _label;
public override string label
{
get { return _label; }
set
{
_label = value;
NotifyPropertyChanged("label");
}
}
}
Update
so I found out that if I do _selectedPickupPOI.label = value.label; it will update the view correctly. What am I doing wrong?
I'm trying to populate a list view control on a XAML page in a Win8 application. I've added the following attributes to the page XAML:
<common:LayoutAwarePage x:Class="SecAviTools.ViewWeatherHome"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="using:MyNameSpace.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:MyNameSpace"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="using:MyNameSpace.Win8ViewModel"
x:Name="pageRoot"
DataContext="{Binding DefaultViewModel,
RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<!-- ... -->
<ListView ItemsSource="{Binding Path=viewmodel:Stations}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Id}"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My source class is:
namespace MyNameSpace.Win8ViewModel
{
public class Stations : INotifyPropertyChanged, INotifyCollectionChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged<T>(NotifyCollectionChangedAction action, ObservableCollection<T> items)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items));
}
public Stations()
{
AllStations = new ObservableCollection<Station>();
AddStations(new List<Station>());
}
public ObservableCollection<Station> AllStations { get; private set; }
public void AddStations(List<Station> stations)
{
AllStations.Clear();
foreach (var station in stations)
AllStations.Add(station);
OnCollectionChanged(NotifyCollectionChangedAction.Reset, AllStations);
OnPropertyChanged("AllStations");
}
}
public class Station
{
public int Id { get; set; }
public string Name { get; set; }
}
}
There is also a button on the page (not shown here) that does the following:
public sealed partial class MyPage : MyNameSpace.Common.LayoutAwarePage
{
private Stations m_Stations = new Stations();
//...
private async void SearchButtonClick(object sender, RoutedEventArgs e)
{
var list = new List<Station>();
list.Add(new Station() { Id = 0, Name = "Zero" });
list.Add(new Station() { Id = 1, Name = "One" });
m_Stations.AddStations(list);
}
}
However, when I run the code, nothing appears in the list view. What am I missing?
TIA
You don't show what DefaultViewModel is, but I'll assume it's set to be an instance of the class you show, Stations. In that case, you need to binding to be:
<ListView ItemsSource="{Binding Path=AllStations}">
The Path of a binding usually refers to a property somewhere; with no further specification, such as a Source, it's a property on the object that is set to be the DataContext.
Regardless, you don't need the namespace qualifier "viewmodel:".
As an aside, if you do end up binding to the ObservableCollection, you don't need to implement INotifyCollectionChanged, only INotifyPropertyChanged for when the AllStations property is set.
I'm using Microsoft Activity Library Designer; For some reasons I need to use ListBox to show some information in it.But I have a problem with it's ItemsSource binding.My Activity side property is like this:
private ObservableCollection<string> _selectedItems;
public ObservableCollection<string> SelectedItems
{
get
{
if (_selectedItems == null)
{
ObservableCollection<string> items = new ObservableCollection<string>();
return items;
}
return _selectedItems;
}
set
{
_selectedItems = value;
}
}
And my XAML side code is like this:
....
<Button Content="Add Item" HorizontalAlignment="Stretch" Grid.Column="0"
Click="Button_Click" Margin="5, 0, 5, 5"/>
<Button Content="Remove Item" HorizontalAlignment="Stretch" Grid.Column="1"
Click="DelButton_Click" Margin="5, 0, 5, 5"/>
....
<ListBox x:Name="LstSelectedPosts" MinHeight="20" ItemsSource="{Binding Path=ModelItem.Selecteditems, Mode=TwoWay}"/>
....
Now when I try to Add/Remove an item to/from this ListBox in Add Item and Remove Item buttons click event, debugger shows me an error that tells I can't modify the ListBox binding source.
So how can I change this Listbox's Items?
Ok there are some errors in your code that could cause the problem.
In the getter, I think you should have this.
if (_selectedItems == null)
{
_selectedItems = new ObservableCollection<string>();
}
return _selectedItems;
In your version, _selectedItems never get initialized.
In the Xaml code, when you set the ItemSource, you wrote Seleceteditems instead of SelectedItems this error doesn't cause an error when you compile but your listBox doesn't have its itemSource setted to the correct element.
And then, you didn't specify the source in
ItemsSource="{Binding Path=ModelItem.Selecteditems, Mode=TwoWay}
that means the source is by default, the DataContext of your object and that DataContext should be initialized with an object that has a public property named ModelItem which has in turn a public property named Selecteditems.
Hope it works.
Here is a small example.
in my xaml file
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Height="287" HorizontalAlignment="Left" Margin="12,12,0,0" x:Name="LstSelectedPosts" VerticalAlignment="Top" Width="294"
ItemsSource="{Binding Path=SelectedItems, Mode=TwoWay}"/>
<Button Content="Add Item" HorizontalAlignment="Stretch" Click="Button_Click" Margin="321,110,68,170"/>
<Button Content="Remove Item" HorizontalAlignment="Stretch" Click="DelButton_Click" Margin="321,147,68,129"/>
</Grid>
in my xaml.cs file
public partial class MainWindow : Window
{
private CDataContext _myCDataContext;
public MainWindow()
{
InitializeComponent();
_myCDataContext = new CDataContext();
DataContext = _myCDataContext;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_myCDataContext.Add();
}
private void DelButton_Click(object sender, RoutedEventArgs e)
{
_myCDataContext.Remove(LstSelectedPosts.SelectedItem.ToString());
}
}
and my CDataContext class
class CDataContext
{
private int _count = 0;
private ObservableCollection<string> _selectedItems;
public ObservableCollection<string> SelectedItems
{
get
{
if (_selectedItems == null)
{
_selectedItems = new ObservableCollection<string>();
}
return _selectedItems;
}
set
{
_selectedItems = value;
}
}
public void Remove(string s)
{
SelectedItems.Remove(s);
}
public void Add()
{
SelectedItems.Add(_count.ToString());
_count++;
}
}
I need to bind all the check boxes(IsChecked Property) in an StackPannel with a bool value which is defined in a class. Attaching my Xaml with my question, please help
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Height="287" HorizontalAlignment="Left" Margin="78,65,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="309" DataContext="checkFlag">
<CheckBox Content="" Height="71" Name="checkBox1" IsChecked="{Binding Path=Binding.MainPage,Source=checkFlag,Mode=TwoWay}"/>
<CheckBox Content="" Height="71" Name="checkBox2" IsChecked="{Binding Path=Binding.MainPage,Source=checkFlag,Mode=TwoWay}"/>
</StackPanel>
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="78,400,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" />
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="227,400,0,0" Name="button2" VerticalAlignment="Top" Width="160" Click="button2_Click" />
The checkboxes are not getting check/uncheck on set/reset of checkFlag. Should i implement "INotifyPropertyChanged" for the flag Or something else. Adding the class behind also, Please have a look.'
namespace Binding
{
public partial class MainPage : PhoneApplicationPage
{
public bool checkFlag;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
checkFlag = true;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
checkFlag = false;
}
}
it looks to me like you need to implement, as you guess, INotifyPropertyChanged here is a link to the MSDN documentation
here is some demo code....
public class MyData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChangedEventArgs args = new PropertyChangedEventArgs(property);
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
int _someInt = 0;
public int SomeInt
{
get { return _someInt ;}
set { if ( value == _someInt ) return;
_someInt = value;
RaisePropertyChanged("SomeInt");
}
}
string _someString = string.Empty;
public string SomeString
{
get { return _someString ;}
set { if ( value == _someString ) return;
_someString = value;
RaisePropertyChanged("SomeString ");
}
}
}
you set up your private variables, then add a property for each one you want to expose.
in the getter, just return the value... in the setter, check if the value has changed and if so assign the value and raise the property changed event. make sure that the name you are passing in as a string is the same as the property... same capitalization, same spelling, etc.