I have a CarouselView which is bound to an ItemsSource of images.
But I want to change the current Image shown by changing the index of the CarouselView.
I have tried using CarouselView.Position to the index of the element that has to be selected. But unfortunately this does not work.
How can I achieve this?
Thanks
I have tried using CarouselView.Position to the index of the element that has to be selected. But unfortunately this does not work.
Since you're using data binding for ItemsSource of CarouselView, you can implement the INotifyPropertyChanged interface for your image model.
For example:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
x:Class="FormsIssue2.Page1">
<Grid>
<cv:CarouselView ItemsSource="{Binding Zoos, Mode=OneWay}" x:Name="CarouselZoos">
<cv:CarouselView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.RowSpan="2" Aspect="AspectFill" Source="{Binding ImageUrl, Mode=OneWay}" />
<StackLayout Grid.Row="1" BackgroundColor="#80000000" Padding="12">
<Label TextColor="White" Text="{Binding Name, Mode=OneWay}" FontSize="16" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" />
</StackLayout>
</Grid>
</DataTemplate>
</cv:CarouselView.ItemTemplate>
</cv:CarouselView>
</Grid>
</ContentPage>
Code behind:
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
Zoos = new ObservableCollection<Zoo>
{
new Zoo
{
ImageUrl = "http://wallpaper-gallery.net/images/image/image-13.jpg",
Name = "Woodland Park Zoo"
},
new Zoo
{
ImageUrl = "https://s3-us-west-1.amazonaws.com/powr/defaults/image-slider2.jpg",
Name = "Cleveland Zoo"
},
new Zoo
{
ImageUrl = "http://i.stack.imgur.com/WCveg.jpg",
Name = "Phoenix Zoo"
}
};
//CarouselZoos.ItemsSource = Zoos;
this.BindingContext = this;
CarouselZoos.ItemSelected += CarouselZoos_ItemSelected;
}
private void CarouselZoos_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as Zoo;
if (item == null)
return;
item.ImageUrl = "https://3.bp.blogspot.com/-W__wiaHUjwI/Vt3Grd8df0I/AAAAAAAAA78/7xqUNj8ujtY/s1600/image02.png";
}
public ObservableCollection<Zoo> Zoos { get; set; }
}
public class Zoo : INotifyPropertyChanged
{
private string _ImageUrl;
public string ImageUrl
{
get { return _ImageUrl; }
set
{
if (value != _ImageUrl)
{
_ImageUrl = value;
OnPropertyChanged("ImageUrl");
}
}
}
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
{
_Name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Once an item is selected, you can find this item's instance in SelectedItemChangedEventArgs, then you can change the image source of this item.
Update:
Based on our discussion, I think the item sources for your CarouselView for thumbnails and the CarouselView for your larger images are in the same sequential order, then when you select the item in your thumbnails, you can get the position of thumbnails and scroll the CarouselView for larger images like this:
var postion = CarouselThunbnails.Position;
CarouselImages.Position = postion;
Related
I have a Xamarin form where I have a list and two buttons. What I am seeing is that, depending where the buttons are, the model loads differently. Here is my Xaml code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Views.RewardsPage"
Title="{Binding Title}"
xmlns:local="clr-namespace:MyApp.ViewModels"
xmlns:model="clr-namespace:MyApp.Models" x:DataType="local:RewardsViewModel"
x:Name="BrowseItemsPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="650" />
</Grid.RowDefinitions>
<CollectionView x:Name="ItemsListView"
ItemsSource="{Binding Items}"
SelectionMode="None" Grid.Row="1" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" x:DataType="model:RewardModel">
<Label Text="{Binding id, StringFormat='ID: {0}'}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<!--other labels removed for brevity-->
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<StackLayout Orientation="Horizontal">
<Button Text="Previous Month" Command="{Binding PreviousMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
<Button Text="Next Month" Command="{Binding NextMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
</StackLayout>
</CollectionView.Footer>
</CollectionView>
</Grid>
</ContentPage>
The code works fine here. But, if I move the StackLayout from the CollectionView.Footer to its own grid row, like this:
</CollectionView>
<!--other labels removed for brevity-->
</CollectionView>
<StackLayout Orientation="Horizontal" Grid.Row="2">
<Button Text="Previous Month" Command="{Binding PreviousMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
<Button Text="Next Month" Command="{Binding NextMonthCommand}" HorizontalOptions="FillAndExpand"></Button>
</StackLayout>
Then the code in my RewardsViewModel executes in a different order. Here is my RewardsViewModel code (simplified):
[QueryProperty(nameof(CurrentMonth), nameof(CurrentMonth))]
[QueryProperty(nameof(CurrentYear), nameof(CurrentYear))]
public class RewardsViewModel: BaseViewModel
{
public ObservableCollection<RewardModel> Items { get; }
private List<MonthModel> months;
private int _current_year;
private int _current_month;
public Command PreviousMonthCommand { get; }
public Command NextMonthCommand { get; }
public RewardsViewModel()
{
Items = new ObservableCollection<RewardModel>();
PreviousMonthCommand = new Command(OnPrevious, ValidatePrevious);
NextMonthCommand = new Command(OnNext, ValidateNext);
}
public int CurrentYear
{
get
{
return _current_year ;
}
set
{
_current_year = value;
LoadItems();
}
}
public int CurrentMonth
{
get
{
return _current_month;
}
set
{
_current_month= value;
LoadItems();
}
}
public void LoadItems()
{
IsBusy = true;
//do stuff
}
private bool ValidatePrevious()
{
//do stuff to validate and return true or false
}
private bool ValidateNext()
{
//do stuff to validate and return true or false
}
private void OnPrevious()
{
//do stuyff
}
private void OnNext()
{
//do stuff
}
}
Depending on where the buttons reside in the Xaml page, the load events change:
When the buttons are within CollectionView, and the page loads, first the constructor loads then the Query Parameter setters load (CurrentMonth set then CurrentYear set)
When the buttons are outside the CollectionView, first the constructor loads then ValidatePrevious method is called and then ValidateNext method is called.
Why does the placement of the buttons in the Xaml file change the order of operations in my ViewModel? And, how do I ensure that the Query Parameter setters are called first, regardless of where the buttons reside?
Edit:
This is the code, from the previous page, that loads this page, passing in the Query Parameters:
async void OnItemSelected(MonthModel item)
{
if (item == null)
return;
await Shell.Current.GoToAsync($"{nameof(RewardsPage)}?{nameof(RewardsViewModel.CurrentYear)}={CurrentYear}&CurrentMonth={SelectedItem.month}");
}
Edit: Adding Base Class:
public class BaseViewModel : INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#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
}
I have a problem with the listviewItem, is that when you change the data if they do it but they are not saved in the interface when you click on another item
This problem happens when binding the textbox to the listviewItem
MainPage.xaml
<Grid RequestedTheme="Light">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="818*" />
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBox
x:Name="titulo"
Grid.Row="0"
FontSize="40"
PlaceholderText="Ingresa tu titulo"
KeyDown="Titulo_KeyDown"
/>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ListView
x:Name="listNotas"
Width="450"
Background="DimGray"
SelectionChanged="ListNotas_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Text="{Binding title, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<RichEditBox
x:Name="editor"
Width="760"
HorizontalAlignment="Stretch" />
</StackPanel>
<GridView
Name="stpanel"
Grid.Row="2"
Height="50">
<TextBlock Text="" Name="Tester"/>
</GridView>
MainPage.xaml.cs
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json" );
public ObservableCollection<Notes> Mynotes;
public MainPage()
{
this.InitializeComponent();
// Load data of Notas.json to Listview
LoadUpdate();
}
private void LoadUpdate()
{
using (StreamReader file = File.OpenText(editpath))
{
var json = file.ReadToEnd();
baseNotes mainnotes = JsonConvert.DeserializeObject<baseNotes>(json);
Mynotes = new ObservableCollection<Notes>();
foreach (var item in mainnotes.notes)
{
Mynotes.Add(new Notes { title = item.title });
}
listNotas.ItemsSource = null;
listNotas.ItemsSource = Mynotes;
listNotas.SelectedIndex = 0;
}
}
private void ListNotas_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string json = File.ReadAllText(editpath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
titulo.Text = jsonObj["notes"][listNotas.SelectedIndex]["title"];
}
private void Titulo_KeyDown(object sender, KeyRoutedEventArgs e)
{
#region
string json = File.ReadAllText(editpath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
int indice = listNotas.SelectedIndex;
jsonObj["notes"][indice]["title"] = titulo.Text;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj);
File.WriteAllText(editpath, output);
// Show json file text in RicheditBox
editor.TextDocument.SetText(Windows.UI.Text.TextSetOptions.None, output);
//Problem
Binding myBinding = new Binding();
myBinding.Source = Mynotes[indice];
myBinding.Path = new PropertyPath("title");
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(titulo, TextBox.TextProperty, myBinding);
#endregion
}
Model: Notes.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
namespace Realtimejsonedit
{
public class Notes : INotifyPropertyChanged
{
public int created { get; set; }
//public string title { get; set; }
private string Title;
public string title
{
get { return Title; }
set {
Title = value;
NotifyPropertyChanged("title");
}
}
public string text { get; set; }
public int id { get; set; }
public int updated { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class baseNotes
{
public List<Notes> notes { get; set; }
}
}
as I say the problem as I am doing the binding but when executing ListNotas.SelectionChanged the values that were saved in the json file are changed, but they do not remain in the listviewitem, although the binding is in the Keydown event and not in ListNotas. SelectionChanged.
the problem:
https://i.imgur.com/IGcd8iz.gif
What I want to achieve:
https://i.imgur.com/KnkbQw9.gif
UWP - How to save ListViewItem state if the data source has changed?
The problem is that you set bind repeatedly in Titulo_KeyDown event. For your requirement, you could bind ListView SelectItem once. For more please refer the following steps:
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json");
public ObservableCollection<Notes> Mynotes { get; set; }
public ViewModel()
{
LoadUpdate();
SetSelectIndex(0);
}
private void SetSelectIndex(int index)
{
SelectItem = Mynotes[index];
}
private void LoadUpdate()
{
using (StreamReader file = File.OpenText(editpath))
{
var json = file.ReadToEnd();
baseNotes mainnotes = JsonConvert.DeserializeObject<baseNotes>(json);
Mynotes = new ObservableCollection<Notes>();
foreach (var item in mainnotes.notes)
{
Mynotes.Add(new Notes { title = item.title });
}
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Notes _selectItem;
public event PropertyChangedEventHandler PropertyChanged;
public Notes SelectItem
{
get
{
return _selectItem;
}
set
{
_selectItem = value;
OnPropertyChanged();
}
}
}
Xaml
<Page.DataContext>
<local:ViewModel />
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="818*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBox
x:Name="titulo"
Grid.Row="0"
FontSize="40"
PlaceholderText="Ingresa tu titulo"
Text="{Binding SelectItem.title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="Titulo_TextChanged"
/>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ListView
x:Name="listNotas"
Width="450"
Background="DimGray"
ItemsSource="{Binding Mynotes}"
SelectedItem="{Binding SelectItem, Mode=TwoWay}"
>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<RichEditBox
x:Name="editor"
Width="760"
HorizontalAlignment="Stretch"
/>
</StackPanel>
<GridView
Name="stpanel"
Grid.Row="2"
Height="50"
>
<TextBlock Name="Tester" Text="" />
</GridView>
</Grid>
Code behind (write the data to json)
public sealed partial class MainPage : Page
{
private dynamic jsonObj;
public string editpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Notas.json");
public ObservableCollection<Notes> Mynotes;
public MainPage()
{
this.InitializeComponent();
string json = File.ReadAllText(editpath);
jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
}
private void Titulo_TextChanged(object sender, TextChangedEventArgs e)
{
#region
int indice = listNotas.SelectedIndex;
jsonObj["notes"][indice]["title"] = titulo.Text;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj);
editor.TextDocument.SetText(Windows.UI.Text.TextSetOptions.None, output);
File.WriteAllText(editpath, output);
#endregion
}
}
This is sample project.
What i am doing is passing data through more than 2 pages. I assign viewmodel to next page while i am navigating. In second page i have a listview that is not refreshing/updating after adding a value.
Help me please!!
Here is my code
MyViewModel
public class MyViewModel : INotifyPropertyChanged
{
public string _userName { get; set; }
public List<family> familyList;
public List<family> FamilyList
{
get { return familyList; }
set
{
familyList = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MyViewModel()
{
_userName = "Mak";
familyList = new List<family>();
}
public void AddMember(string memberName)
{
FamilyList.Add(new family
{
name = memberName,
id = Guid.NewGuid().ToString(),
username=_userName
});
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
userdetails.xaml
<cl:BasePage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="familyinfo.userdetails" xmlns:cl="clr-namespace:familyinfo;assembly=familyinfo">
<Label Font="Roboto-Medium" FontSize="14" Text="{Bindinbg _userName}" />
<Button Clicked="Next_Step" HeightRequest="30" HorizontalOptions="FillAndExpand" BorderRadius="12" Text="NEXT" />
</cl:BasePage>
userdetails.xaml.cs
public partial class userdetails : BasePage
{
public MyViewModel _myViewModel { get; set; }
public userdetails()
{
InitializeComponent();
BindingContext = new MyViewModel();
}
void Next_Step(object sender, System.EventArgs e)
{
_myViewModel =(MyViewModel) this.BindingContext;
var familyMember = new FamilyMember();
familyMember.BindingContext = _myViewModel;
Application.Current.MainPage = new NavPage(registerCar);
}
}
FamilyMember.xaml
<cl:BasePage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="familyinfo.FamilyMember" xmlns:cl="clr-namespace:familyinfo;assembly=familyinfo">
<Label Font="Roboto-Medium" FontSize="14" Text="{Bindinbg _userName}" />
<cl:CustomEntry x:Name="txtMemberName" Placeholder="Member Name" FontSize="12" />
<Button Clicked="AddMember" HeightRequest="30" HorizontalOptions="FillAndExpand" BorderRadius="12" Text="Add" />
<ListView ItemsSource="{Binding FamilyList}" VerticalOptions="FillAndExpand" BackgroundColor="Transparent">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid Padding="20,10,0,0" ColumnSpacing="12" RowSpacing="0" BackgroundColor="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto">
</ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto">
</RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Text="{Binding name}" Grid.Column="0" Font="Roboto-Medium" FontSize="14" TextColor="#000000" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</cl:BasePage>
FamilyMember.xaml.cs
public partial class FamilyMember : BasePage
{
public MyViewModel _myViewModel { get; set; }
public userdetails()
{
InitializeComponent();
}
void AddMember(object sender, System.EventArgs e)
{
_myViewModel = (MyViewModel)this.BindingContext;
_myViewModel.AddMember(txtMemberName.Text);
}
}
I agree with Atul: Using an ObservableCollection is the right way to do it.
A workaround - if you don't have a chance to change that - is to set the ListView's ItemSource to null and back to the list, whenever data changed and the UI needs to update:
void UpdateListView(ListView listView)
{
var itemsSource = listView.ItemsSource;
listView.ItemsSource = null;
listView.ItemsSource = itemsSource;
}
In fact, you must use a collection that implements INotifyCollectionChanged interface (instead of the well known INofifyPropertyChanged). And that's exactly what does ObservableCollection<T> for you. This is why it works like "magic".
I just used ObservableCollection instead of List and it works!!
I am developing an enterprise application using xamarin.forms. It has been few days ListView's Memory leak issue become a nightmare for me. For the sake of simplicity I'll try to explain with sample code.
XAML Page Code - Page with ListView and two Button(Add & Remove)
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ListViewTest"
x:Class="ListViewTest.MainPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ListView Grid.Row="0" BackgroundColor="White" x:Name ="ItemsListView">
<ListView.ItemTemplate>
<DataTemplate >
<TextCell TextColor="Black" Text="{Binding ItemText}"
DetailColor="Black" Detail="{Binding ItemDetail}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Grid.Row="1" Text="Add" Clicked="AddItemClicked"/>
<Button Grid.Row="2" Text="Remove" Clicked="RemoveItemClicked"/>
</Grid>
</ContentPage>
C# Code Behind - Just adding and removing objects from collection.
public partial class MainPage : ContentPage
{
ObservableCollection<SampleData> itemsListCollection;
public MainPage()
{
InitializeComponent();
itemsListCollection = new ObservableCollection<SampleData>();
ItemsListView.ItemsSource = itemsListCollection;
}
void AddItemClicked(object sender, EventArgs e)
{
SampleData data = new SampleData();
data.ItemText = "An Item";
data.ItemDetail = "Item - " + (itemsListCollection.Count + 1).ToString();
itemsListCollection.Add(data);
}
void RemoveItemClicked(object sender, EventArgs e)
{
SampleData item = (SampleData)ItemsListView.SelectedItem;
if (item != null)
{
itemsListCollection.Remove(item);
}
}
}
Data class - Just two properties
class SampleData
{
public event PropertyChangedEventHandler PropertyChanged;
private string itemText;
public string ItemText
{
get
{
return itemText;
}
set
{
itemText = value;
NotifyPropertyChanged("ItemText");
}
}
private string itemDetail;
public string ItemDetail
{
get
{
return itemDetail;
}
set
{
itemDetail = value;
NotifyPropertyChanged("ItemDetail");
}
}
private void NotifyPropertyChanged(string propName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
}
Add Button - Adds item to ListView
Remove Button - Removes item from ListView
Problem -
Add some items to list.
Remove few or all.
All previously added SampleData objects remain in memory even after all the items have
been removed using Remove button.
Image - Memory Snapshot of original application
Image - Detailed Memory Snapshot of sample application
I have a MVVM setup that creates a View on my MainWindow. I am not sure how to know when a user Clicks on a specific Notification Item inside the View. Where would I add the event, or a command to know when that happens?
here are is my MVVM code :
MainWindow
cs:
NotificationViewModel notificationViewModel = new NotificationViewModel();
notificationViewModel.AddNoticiation(new NotificationModel() { Message = "Error", Name = "Station 21" });
NotificationView.DataContext = notificationViewModel;
xaml:
<notification:NotificationView x:Name="NotificationView" />
NotificationModel
public class NotificationModel : INotifyPropertyChanged
{
private string _Message;
public string Message
{
get { return _Message; }
set
{
if (_Message != value)
{
_Message = value;
RaisePropertyChanged("Message");
}
}
}
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name != value)
{
_Name = value;
RaisePropertyChanged("Name");
}
}
}
public string TimeStamp
{
get { return DateTime.Now.ToString("h:mm:ss"); }
}
#region PropertChanged Block
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion
}
NotificationViewModel
public class NotificationViewModel
{
private ObservableCollection<NotificationModel> _Notifications = new ObservableCollection<NotificationModel>();
public ObservableCollection<NotificationModel> Notifications
{
get { return _Notifications; }
set { _Notifications = value; }
}
public void AddNoticiation(NotificationModel notification)
{
this.Notifications.Insert(0, notification);
}
}
NotificationView
<Grid>
<StackPanel HorizontalAlignment="Left" >
<ItemsControl ItemsSource="{Binding Path=Notifications}"
Padding="5,5,5,5">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="SlateGray"
CornerRadius="4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Path=TimeStamp}" />
<TextBlock Grid.Column="1"
Text="{Binding Path=Name}" />
<TextBlock Grid.Column="2"
Text="{Binding Path=Message}" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
There's no real selection mechanism built into an ItemsControl. It would probably solve your problem to switch out your ItemsControl for a ListBox.
If you do that, you can bind to SelectedItem, then handle any changes made to SelectedItem using the PropertyChanged event.
Example:
In your view model's constructor:
PropertyChanged += NotificationViewModel_PropertyChanged;
Add a property to your view model to allow the binding:
private string _selectedNotification;
public string SelectedNotification
{
get { return _selectedNotification; }
set
{
if (_selectedNotification != value)
{
_selectedNotification = value;
RaisePropertyChanged("SelectedNotification");
}
}
}
Finally, add the event handler to your view model:
NotificationViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e))
{
if (e.PropertyName = "SelectedNotification") DoStuff();
}
You may find that you don't even need to hook into PropertyChanged if you just want to update another control in your view based on the selected item in your list box. You can just bind directly to the property within xaml.