I'm trying to make control to add contacts Which has a TreeView. When I add contacts to the control displays nothing in the treeView. Here I show the code:
<TreeView x:Name="TvContactos" ItemsSource="{Binding Path=Groups}" HorizontalContentAlignment="Stretch" DockPanel.Dock="Left" ScrollViewer.CanContentScroll="True">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ViewModelGroupContact}" ItemsSource="{Binding Children}">
<Grid Height="35">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding GroupName}" Style="{StaticResource BloStyle}" Grid.Column="0"/>
</Grid>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelContact}">
<Grid Height="38">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Height="32" Width="32" Source="Resources/User.jpg" Margin="3" Grid.Column="0"/>
<TextBlock Text="{Binding ContactName}" Style="{StaticResource BloStyle}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</TreeView.Resources>
<TreeView.DataContext>
<local:ViewModelGroups/>
</TreeView.DataContext>
</TreeView.Resources>
In the code behind I have the following classes
public class ViewModelGroups : INotifyPropertyChanged
{
ObservableCollection<ViewModelGroupContact> _groups;
public ViewModelGroups()
{
Groups = new ObservableCollection<ViewModelGroupContact>();
}
public ObservableCollection<ViewModelGroupContact> Groups
{
get { return _groups; }
set
{
_groups = value;
OnPropertyChanged("Groups");
}
}
public void AddGroup(string groupName,RosterItem contact)
{
var newContact = new Contact {Name = contact.Name ?? contact.Jid.ToString(), RosterItem = contact};
var vmc = _groups.FirstOrDefault(item => item.GroupName == groupName);
if (vmc == null)
{
var contGroup = new ContactGroup { Name = groupName };
vmc = new ViewModelGroupContact(contGroup);
}
vmc.AddContactToGroup(newContact);
Dispatcher.CurrentDispatcher.BeginInvoke((new Action(() => Groups.Add(vmc))));
OnPropertyChanged("Groups");
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ViewModelGroupContact : TreeViewItemViewModel
{
private readonly ContactGroup _contactGroup;
public string GroupName { get; set; }
public ViewModelGroupContact(ContactGroup contactGroup)
: base(null, true)
{
_contactGroup = contactGroup;
GroupName = _contactGroup.Name;
}
protected override void LoadChildren()
{
foreach (Contact contact in _contactGroup.GetContacts())
base.Children.Add(new ViewModelContact(contact, this));
}
public void AddContactToGroup(Contact contact)
{
if (!_contactGroup.GetContacts().Contains(contact))
_contactGroup.AddContactToGroup(contact);
}
}
public class ViewModelContact:TreeViewItemViewModel
{
private readonly Contact _contact;
public ViewModelContact(Contact contact, ViewModelGroupContact group)
: base(group, true)
{
_contact = contact;
}
public string ContactName
{
get { return _contact.Name; }
}
}
When added a contact to treeview nothing is displayed.No show TreeViewItemViewModel class which inherits from INotifyPropertyChanged for not doing longer the post. This class has a property called Childrens.
This is the control class that was missing
public partial class ContactControl : UserControl
{
#region Private
private ViewModelGroups _viewModel;
private const string MDefaultGroupName = "ungrouped";
#endregion
public ContactControl()
{
InitializeComponent();
Init();
}
public ViewModelGroups ViewModel
{
get { return _viewModel; }
}
public void Init()
{
_viewModel = new ViewModelGroups();
TvContactos.DataContext = _viewModel;
}
public void AddContact(RosterItem ritem)
{
string groupname;
if (ritem.GetGroups().Count > 0)
{
var g = (Group)ritem.GetGroups().Item(0);
groupname = g.Name;
}
else
{
groupname = MDefaultGroupName;
}
_viewModel.AddGroup(groupname, ritem);
}
}
Related
I have two pages: MainPage and FilterPage(modal page).
with their respective Viewmodels: MainViewModel and FilterViewModel.
In MainPage I have a listview that's populated with data from an API. The data is passed to the FilterPage where it is filtered by some specific criteria. In the end a new list is created which is assigned to the binded variable of the listview. What I noticed is that after the modal page closes the listview's items arent updated. What is the proper way to do this?
Model:
public class Multilist
{
public string Title { get; set; }
public string Date { get; set; }
public string Status { get; set; }
public string Customer { get; set; }
}
MainViewModel:
public class MainViewModel : INotifyPropertyChanged
{
private IList<Multilist> mainList = new List<Multilist>();
public IList<Multilist> MainList
{
get => mainList;
set
{
if (value == mainList)
return;
mainList = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public MainViewModel(INavigation navigation)
{
this._navigation = navigation;
Task.Run(async () => await GetData());
GotoFilterPageCommand = new AsyncCommand(GotoFilterPage);
}
private async Task GetData()
{
//Gets data from API
MainList = data;
}
private async Task GotoFilterPage()
{
await this._navigation.PushModalAsync(new FilterPage(MainList.ToList()), true);
}
}
FilterViewModel:
public class FilterViewModel : INotifyPropertyChange
{
public List<Multilist> OldList { get; set; }
private IList<Multilist> mainList = new List<Multilist>();
public IList<Multilist> MainList
{
get => mainList;
set
{
if (value == mainList)
return;
mainList = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public FilterViewModel(List<Multilist> oldlist)
{
Oldlist = oldlist;
SomeCommand = new AsyncCommand(SomeTask);
}
private async Task SomeTask()
{
// Some code here
CreateNewList(OldList);
}
private async Task CreateNewList(List<Multilist> oldlist)
{
//Some code here --> newMainList
pageA.MainList = newMainList;
await App.Current.MainPage.Navigation.PopModalAsync();
}
}
The listview in MainPage:
<ListView x:Name="TestListView"
ItemsSource="{Binding MainList}"
Grid.Row="4" Grid.ColumnSpan="3"
HasUnevenRows="True"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="0,0,0,1">
<Grid VerticalOptions="Fill" Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="{Binding Title}" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="0" />
<Label Text="{Binding Date}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="1" />
<Label Text="{Binding Customer}" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="2" />
<Label Text="{Binding Status}" HorizontalTextAlignment="End" VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="3" />
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You could use Singleton to make a global instance for both MainViewModel and FilterViewModel.
I make a simple example for your reference.
Model:
public class Person
{
public string Name { get; set; }
public string FirstName { get; set; }
public int Age { get; set; }
}
ViewModel:
public class PersonViewModel
{
#region Singleton Pattern
private PersonViewModel()
{
Persons = new ObservableCollection<Person>()
{
new Person(){ Name="A"},
new Person(){ Name="A2"},
new Person(){ Name="A3"},
new Person(){ Name="A4"},
};
}
public static PersonViewModel Instance { get; } = new PersonViewModel();
#endregion
private ObservableCollection<Person> _person;
public ObservableCollection<Person> Persons
{
get { return _person; }
set { _person = value; }
}
}
Page24: //MainPage
<StackLayout>
<ListView ItemsSource="{Binding MainList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Navigate To FilterPage" Clicked="Button_Clicked">
</Button>
</StackLayout>
Page24 Code behind:
public Page24()
{
InitializeComponent();
this.BindingContext = new Page24ViewModel();
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new FilterPage());
}
Page24ViewModel://MainViewModel
public class Page24ViewModel : INotifyPropertyChanged
{
private PersonViewModel _personViewModel;
public Page24ViewModel()
{
_personViewModel = PersonViewModel.Instance;
}
private ObservableCollection<Person> mainList;
public ObservableCollection<Person> MainList
{
get { return _personViewModel.Persons; }
set
{
mainList = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
FilterPage:
<ContentPage.Content>
<StackLayout>
<StackLayout>
<Label Text="Name:"></Label>
<Entry x:Name="entry"></Entry>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Button x:Name="btn_Add" Text="Add" Clicked="btn_Add_Clicked"></Button>
<!--<Button x:Name="btn_Delete" Text="Delete" Clicked="btn_Delete_Clicked"></Button>-->
</StackLayout>
<ListView ItemsSource="{Binding MainList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
FilterPage code behind:
private PersonViewModel _personViewModel;
public FilterPage()//modal page
{
InitializeComponent();
this.BindingContext = new FilterViewModel();
}
private void btn_Add_Clicked(object sender, EventArgs e)
{
_personViewModel = PersonViewModel.Instance;
_personViewModel.Persons.Add(new Person() { Name = entry.Text });
}
FilterViewModel:
public class FilterViewModel : INotifyPropertyChanged
{
private PersonViewModel _personViewModel;
public FilterViewModel()
{
_personViewModel = PersonViewModel.Instance;
}
private ObservableCollection<Person> newMainList;
public ObservableCollection<Person> MainList
{
get { return _personViewModel.Persons; }
set
{
newMainList = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I have a problem. I created a ListView with a ViewModel. In my ListView I have a few Labels with Text that is bound to the objects in the ItemSource. Now when I change a value in the ViewModel of an item in the ObservableCollection, nothing changes on the screen!
Here is my ViewModel:
public class VM_DeviceList : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand cmdDeleteDevice
{
get
{
return new Command<int>((x) => DeleteDevice_Handler(x));
}
}
public ICommand cmdTogglePower
{
get
{
return new Command<int>((x) => TogglePower_Handler(x));
}
}
private ObservableCollection<DisplayedDevice> _knownDeviceList;
public ObservableCollection<DisplayedDevice> knownDeviceList
{
get
{
return _knownDeviceList;
}
set
{
if (_knownDeviceList != value)
{
_knownDeviceList = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("knownDeviceList"));
}
}
}
}
Here is my class for the ObservableCollection:
public class DisplayedDevice : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int Id { get; set; }
public string Name { get; set; }
public string State { get; set; }
public string StateShown { get; set; }
public string deviceImage { get; set; }
public string Color { get; set; }
public string PowerStateColor { get; set; }
public string DeviceImageColor { get; set; }
public string DeviceImage
{
get
{
return deviceImage;
}
set
{
deviceImage = value;
OnPropertyChanged();
}
}
}
And here is the xaml:
<ListView ItemsSource="{Binding knownDeviceList}" SelectionMode="None" RowHeight="90" ItemTapped="device_Clicked" x:Name="MyListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<AbsoluteLayout HeightRequest="70" Margin="20,10,20,10">
<StackLayout Opacity="0.3" BackgroundColor="White"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All" />
<StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All">
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="70" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" Margin="0,3,0,0"
FontAttributes="Bold" FontSize="24" TextColor="White" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding StateShown}" FontSize="18" TextColor="White" />
<!-- <Image Source="power" Grid.RowSpan="2" Grid.Column="2" Margin="5" /> -->
<controls:IconView x:Name="btnPower" Source="power" Grid.RowSpan="2" Grid.Column="2" Margin="5"
Foreground="{Binding PowerStateColor}">
<controls:IconView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.cmdTogglePower, Source={x:Reference MyListView}}" CommandParameter="{Binding Id}" />
</controls:IconView.GestureRecognizers>
</controls:IconView>
</Grid>
</StackLayout>
</AbsoluteLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now what I expected to happen, was that when I click on the IconView I change the IconView color and the label with the StateShown binding. But nothing changes when I click on the IconView!
What am I doing wrong?
Add OnPropertyChanged method call to every property on DisplayedDevice for those you want the UI to notice.
public class DisplayedDevice : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
private int id;
public int Id
{
get
{
return id;
}
set
{
Id = value;
OnPropertyChanged();
}
}
private string state;
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged();
}
}
......
}
You can use MVVMHelpers NuGet Package and implement the ObservableObject Class from the NuGet directly to DisplayedDevice class and you need to make all the properties with a reference to the private variable.
public class DisplayedDevice: ObservableObject
{
string _textField = string.Empty;
public string TextField
{
get => _textField;
set => SetProperty(ref _textField, value);
}
bool _isBarChartVisible = false;
public bool IsBarChartVisible
{
get => _isBarChartVisible;
set => SetProperty(ref _isBarChartVisible, value);
}
}
}
Every public property should be backed by a private property of the same type. This is important so that any change in any property will be reflected on the UI using INotifyPropertyChanged
I have a Grouped ListView bound to MyGroup (see below) that contains some property but when that property is being changed the view is not getting updated although I call OnPropertyChanged
My Class
public class MyGroup : ObservableCollection<Items>, INotifyPropertyChanged
{
private string foo;
public string Foo
{
get => foo;
set
{
foo = value;
OnPropertyChanged(nameof(Foo));
}
}
...
}
My View
<ListView ItemSource="{Binding GroupList}">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="#2196F3"
Padding="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label FontSize="Medium" Text="{Binding Header}" Grid.Column="1" FontAttributes="Italic" TextColor="White"/>
<Label Text="{Binding Foo}" TextColor="White" FontSize="Medium" Grid.Column="1" HorizontalOptions="End"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>
MyViewModel
public List<MyGroup> GroupList { get => groupList ;set => SetProperty(ref groupList, value); }
According to your description, you bind Foo in ListView Group header, you said that group header don't update when property changed, but I don't see where do you change this property. I've written a sample - I changed the first item group header when button click.
public class PersonList1 : ObservableCollection<Person1>, INotifyPropertyChanged
{
private string _heading;
public string Heading
{
get { return _heading; }
set
{
_heading = value;
RaisePropertyChanged("Heading");
}
}
public ObservableCollection<Person1> Persons => this;
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Person1
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string DisplayName
{
get
{
return $"{LastName}, {FirstName}";
}
}
}
<StackLayout>
<ListView IsGroupingEnabled="true" ItemsSource="{Binding ListOfPeople}">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Heading}" />
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding DisplayName}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button
x:Name="btn1"
Clicked="Btn1_Clicked"
Text="change header" />
</StackLayout>
public partial class Page15 : ContentPage
{
public ObservableCollection<PersonList1> ListOfPeople { get; set; }
public Page15 ()
{
InitializeComponent ();
var sList = new PersonList1()
{
new Person1() { FirstName = "Sally", LastName = "Sampson" },
new Person1() { FirstName = "Taylor", LastName = "Swift" },
new Person1() { FirstName = "John", LastName = "Smith" }
};
sList.Heading = "S";
var dList = new PersonList1()
{
new Person1() { FirstName = "Jane", LastName = "Doe" }
};
dList.Heading = "D";
var jList = new PersonList1()
{
new Person1() { FirstName = "Billy", LastName = "Joel" }
};
jList.Heading = "J";
ListOfPeople = new ObservableCollection<PersonList1>()
{
sList,
dList,
jList
};
this.BindingContext = this;
}
private void Btn1_Clicked(object sender, EventArgs e)
{
ListOfPeople[0].Heading = "this is test";
}
}
You can see the the first ListView group header property change as "this is test".
Your question is not complete enough. Please add detail of how you are calling/setting values to property.
BTW, instead of using this(a wild guess,as you have not provided enough info):
public List<MyGroup> GroupList { get => groupList ;set => SetProperty(ref groupList, value); }
try this:
public List<MyGroup> GroupList {
get{
return groupList;
}
set{
groupList = value;
OnPropertyChanged(nameof(GroupList));
}
ViewModel
public class MainWindowViewModel:BindableBase
{
public IRelayCommand MyCommand { get; protected set; }
private void CreateCommand()
{
this.MyCommand = new RelayCommand(MyCommandExecuted, CanExecuteMyCommand);
}
private void MyCommandExecuted(object obj)
{
MessageBox.Show("Command Executed");
}
private bool CanExecuteMyCommand(object obj)
{
return true; // The value is based on Selected Item
}
}
XAML
<ListBox
x:Name="myListBox"
ItemsSource="{Binding Path=MyClass}"
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Path=HeaderName}" IsExpanded="True">
<StackPanel>
<DataGrid
x:Name="dataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=RowVal}" SelectedItem="{Binding CurrentItem}"/>
</StackPanel>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
<Button Content="Select"
Command="{Binding Path=MyCommand }"
CommandParameter="{Binding ElementName=myListBox,Path=SelectedItem}"/>
DataClass
public class DataClass
{
public string HeaderName { get; set; }
public object RowVal { get; set; }
public ObservableCollection<DataGridColumn> ColumnCollection { get; set;}
private object currentItem;
public object CurrentItem
{
get
{
return currentItem;
}
set
{
currentItem = value;
}
}
}
How can I bind my button to Listbox item which is CurrentItem in DataClass ?
I created a complete example to show how I would do it. You would have to bind the parent element to SelectedItem as well, to keep track of when that item changes. Since the SelectedItem is public in your child class as well you can access that when your command triggers in your main view model.
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Parents}" SelectedItem="{Binding SelectedParent}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type local:Parent}">
<DataGrid ItemsSource="{Binding Children}" SelectedItem="{Binding SelectedChild}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Grid.Row="1" Width="70" Content="Click me" Height="25" Command="{Binding MyCommand}" />
i.e. in DoWork, you can get the child from the parent via its public property.
public sealed class WindowViewModel : INotifyPropertyChanged
{
private readonly ObservableCollection<Parent> parents;
private readonly ICommand myCommand;
private Parent selectedParent;
public WindowViewModel()
{
parents = new ObservableCollection<Parent>
{
new Parent{ Name = "P1"},
new Parent{ Name = "P2"}
};
myCommand = new DelegateCommand(DoWork);
}
private void DoWork()
{
var selectedChild = SelectedParent == null ? null : SelectedParent.SelectedChild;
}
public Parent SelectedParent
{
get { return selectedParent; }
set
{
if (selectedParent == value)
return;
selectedParent = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Parent> Parents
{
get { return parents; }
}
public ICommand MyCommand
{
get { return myCommand; }
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
With the basic setup of Data models
public class Parent : INotifyPropertyChanged
{
private ObservableCollection<Child> children;
private Child m_SelectedChild;
public Parent()
{
children = new ObservableCollection<Child>
{
new Child {Name = "C1"},
new Child {Name = "C2"}
};
}
public string Name { get; set; }
public ObservableCollection<Child> Children
{
get { return children; }
}
public Child SelectedChild
{
get { return m_SelectedChild; }
set
{
if (m_SelectedChild == value)
return;
m_SelectedChild = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Child
{
public string Name { get; set; }
}
Another solution, if you are more interested of the Child item in your WindowViewModel is to change the relative source of where the binding should occur, in your DataGrid. i.e., the binding would look like this instead:
<DataGrid ItemsSource="{Binding Children}"
SelectedItem="{Binding DataContext.SelectedChild, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" />
and then move the Property from Parent to WindowViewModel. With that you would be able to trigger changes to your button command when the child element changes for any of the Parent elements.
I think you want to pass the CurrentItem to the MyCommand as CommandParameter right?
Then you only have to:
CommandParameter="{Binding CurrentItem, UpdateSourceTrigger=PropertyChanged}"
Try this :
CommandParameter="{Binding ElementName=myListBox,Path=SelectedItem.CurrentItem}"
How to select only one of lists which is a part of List FeatList? One FeatList item consists of CurrencyTypes and Date. I need to add to CurList only that CurrencyTypes list where Date is equal to ListCtrl3.SelectedItem.
namespace PhoneApp1
{
public class CurrencyOfDate
{
public List<CurrencyType> CurrencyTypes;
public string Date { get; set; }
public override string ToString()
{
return Date;
}
}
public class CurrencyType
{
public string Name { get; set; }
public string Value { get; set; }
public override string ToString()
{
return Name;
}
}
public partial class MainPage : PhoneApplicationPage
{
List<CurrencyType> curList = new List<CurrencyType>();
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public List<CurrencyType> CurList
{
get { return curList; }
set
{
curList = value;
InvokePropertyChanged("CurList");
}
}
List<CurrencyOfDate> featList = new List<CurrencyOfDate>();
public List<CurrencyOfDate> FeatList
{
get { return featList; }
set
{
featList = value;
InvokePropertyChanged("FeatList");
}
}
// Constructor
public MainPage()
{
InitializeComponent();
Dispatcher.BeginInvoke(() =>
{
_download_serialized_data("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml");
});
}
private async void _download_serialized_data(string url)
{
HttpClient webclient = new HttpClient();
try
{
var downloadedString =
await
webclient.GetStringAsync(
new Uri("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml"));
XElement xmlData = XElement.Parse(downloadedString);
XNamespace ns = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
List<CurrencyOfDate> list = new List<CurrencyOfDate>();
foreach (XElement c in xmlData.Elements(ns + "Cube").Elements(ns + "Cube"))
list.Add(new CurrencyOfDate()
{
Date = c.Attribute("time").Value,
CurrencyTypes =
(from k in xmlData.Elements(ns + "Cube").Elements(ns + "Cube").Elements(ns + "Cube")
select new CurrencyType()
{
Name = k.Attribute("currency").Value,
Value = k.Attribute("rate").Value
}).ToList()
});
FeatList = list;
ListCtrl3.ItemsSource = FeatList;
foreach (var selItem in list.Where(selItem => ListCtrl3.SelectedItem.ToString() == selItem.Date))
{
CurList = selItem.CurrencyTypes.ToList();
FromList.ItemsSource = CurList;
break;
}
}
and xaml:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,511">
<toolkit:ListPicker ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding CurList}"
x:Name="FromList"
Margin="10,0,240,0">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock FontSize="30"
Text="{Binding Name}">
</TextBlock>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
</Grid>
<Grid>
<toolkit:ListPicker x:Name="ListCtrl3" ItemsSource="{Binding FeatList}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" HorizontalContentAlignment="Stretch" Margin="230,0,10,0">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding Date}"/>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
</Grid>