can't' bind to ViewModel - c#

I have a problem that resists the past few hours, here is the ViewModel code: (PS: I can not share the url stream but do not worry its march because I tested it with BreakPoint)
private ObservableCollection<CustomerPublic> customers;
List<CustomerPublic> liste = new List<CustomerPublic>();
public ObservableCollection<CustomerPublic> Customers
{
get
{ return customers; }
set
{
if (customers != value)
{
customers = value;
RaisePropertyChanged("Customers");
}
}
}
private int id;
public int ID
{
get
{
return id;
}
set
{
id = value;
RaisePropertyChanged("ID");
}
}
public Detail_AgenceViewModel(int id)
{
this.ID = id;
PopulateCollection();
}
public Detail_AgenceViewModel()
{
}
private void PopulateCollection()
{
ParseFeedRequest();
}
private void ParseFeedRequest()
{
RestClient client = new RestClient();
client.BaseUrl = "....";
RestRequest request = new RestRequest();
.......
client.ExecuteAsync(request, ParseFeedCallBack);
}
public void ParseFeedCallBack(IRestResponse response)
{
if (response.StatusCode == HttpStatusCode.OK)
{
ParseXMLFeed(response.Content);
}
}
private void ParseXMLFeed(string feed)
{
if (feed == null)
return;
XElement xmlItems = XElement.Parse(feed);
liste = (from response in xmlItems.Descendants("result")
let lib = response.Element("lib")
let adresse = response.Element("adresse")
select new CustomerPublic
{
lib = lib == null ? null : lib.Value,
adresse = adresse == null ? null : adresse.Value,
}).ToList();
Customers = new ObservableCollection<CustomerPublic>(liste);
}
the View:
<phone:PhoneApplicationPage.DataContext>
<vm:Detail_AgenceViewModel/>
</phone:PhoneApplicationPage.DataContext>
<Grid x:Name="LayoutRoot"
Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel"
Grid.Row="0"
Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle"
Text="MY APPLICATION"
Style="{StaticResource PhoneTextNormalStyle}" />
<TextBlock x:Name="PageTitle"
Text="page name"
Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}" />
</StackPanel>
<!--ContentPanel - place additional content here-->
<StackPanel x:Name="ContentPanel" Grid.Row="2" Margin="12,0,12,0" Orientation="Vertical">
<!--TextBox Text="{Binding Count, Mode=TwoWay}" x:Name="tbCount" />
<TextBlock Text="{Binding Count}" /-->
<ListBox x:Name="Agences" ItemsSource="{Binding Customers}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding lib}" />
<TextBlock Text="{Binding adresse}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
The problem is that its all going well Customers even she is loaded but nothing appears! someone has an idea?

You are setting Customers to a new instance of an observable collection!
When you change the observable collection to a new instance, you must use INotifyPropertyChanged to tell the view that the collection has changed to a new instance - although changes to the items IN the collection are notified, changes to the collection ITSELF are not.
When you do this:
Customers = new ObservableCollection<CustomerPublic>(liste);
The view is still bound to the OLD collection. You should do:
Customers.Clear();
foreach(var item in liste)
Customers.Add(item);
OR make sure that the Customers property calls the NotifyPropertyChanged function.
Have a check of this video or article for more info:
http://www.codeproject.com/Articles/371217/Apex-Part-1-Create-Your-First-MVVM-Application
http://www.youtube.com/watch?v=m4cx9w5fiwk&feature=youtu.be
Good luck and please do let me know if this helps!!

I'm having similar problems ;
public void FillList(List<StockItem> siList)
{
listBox.ItemsSource = siList;
}
Where sIList is a filled list of X items, with correctly named properties.
Program builds & runs fine, but the listbox isnt shown. (this problem started when transitioning into MVVM)

I've got it.
Check your datacontext - I bet it is null. I've had this exact same issue in WP7. In the constructor of your PhoneApplicationPage do:
DataContext = new Detail_AgenceViewModel();
and initialise it there. In WP7 when I create the datacontext in XAML it's null. Does this help?

Related

Grouped listview crashes on second refresh

I've got a grouped Xamarin ListView, and found it troublesome updating the values within the ListView.
ViewModel;
public class MainViewModel
{
private ObservableCollection<ObservableGroupCollection<string, TaskItem>> _userTasksGrouped;
public ObservableCollection<ObservableGroupCollection<string, TaskItem>> UserTasksGrouped
{
get => _userTasksGrouped;
set
{
_userTasksGrouped = value;
OnPropertyChanged();
}
}
private void PopulateMyTasks()
{
UserTasks.Clear();
foreach (var task in MyTasks)
{
UserTasks.Add(new TaskItem(task));
}
DoTaskGrouping();
}
private void DoTaskGrouping()
{
UserTasksGrouped.Clear();
var groupedData =
UserTasks
.OrderBy(ut => ut.Name)
.GroupBy(ut => ut.StateEnum.ToString())
.Select(ut => new ObservableGroupCollection<string, TaskItem>(ut))
.ToList();
foreach (var group in groupedData)
UserTasksGrouped.Add(group);
}
public void UpdateTaskItem(TaskChanged taskChanged)
{
// update listing
var usertask = UserTasksGrouped.First(utg => utg.Any(ut => ut.Task.Id == taskChanged.Task.Id))
.First(t => t.Task.Id == taskChanged.Task.Id);
if (usertask != null)
{
usertask.Task = taskChanged.Task;
}
else
{
// something else
}
DoTaskGrouping();
}
public MainViewModel()
{
UserTasks = new ObservableCollection<TaskItem>();
UserTasksGrouped = new ObservableCollection<ObservableGroupCollection<string, TaskItem>>();
}
}
XAML;
<ListView
IsGroupingEnabled="true"
ItemsSource="{Binding UserTasksGrouped}"
GroupDisplayBinding="{Binding Key}"
GroupShortNameBinding="{Binding Key}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<BoxView
Grid.Row="0" Grid.RowSpan="2"
Grid.Column="0" Grid.ColumnSpan="2"
MinimumHeightRequest="16"
MinimumWidthRequest="16"
Margin="0"
Color="{Binding StatusColour}" />
<Label
Grid.Row="0" Grid.Column="2"
Text="{Binding Name}"
Style="{StaticResource LabelStyle}"
WidthRequest="200" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Task Item:
public class TaskItem : INotifyPropertyChanged
{
private MyTask _task;
public MyTask Task
{
get => _task;
set
{
_task = value;
Name = value.Name;
OnPropertyChanged();
}
}
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public string StatusColour => "Green"
}
The problem when I call DoTaskGrouping() for the second time to force a refresh the App crashes with an UnhandledException. I managed to track this down to a "An item with the same key has already been added" error, so I added the ViewCell.View thinking it might fix things. Please help, I have a few questions;
How do I fix the error "An item with same key has already been added" and successfully refresh my grouped listing.
Do Observable collections within observable collections update themselves. Meaning if the properties or what their grouped by changes, so does the grouping? Meaning an item can move between groups?
Is my approach sound from a MVVM perspective? TaskItem could do with cleaning up.
Fixed by removing from the MainViewModel
public ObservableCollection<ObservableGroupCollection<string, TaskItem>> UserTasksGrouped
DoTaskGrouping() building the collection afresh each time;
UserTasksGrouped = new ObservableCollection<ObservableGroupCollection<string, TaskItem>>(groupedData);

Binding an Observable Collection to a GridView

My UWP is required to have a Favorites page that allows the user to reorder and save the data on the page. Originally my data comes from a large JSON file which is deserialized using Newtonsoft's Json.net, and is stored for this page in a Dictionary which then fills the public ObservableCollection.
This is where I now get lost, setting the ObservableCollection as the DataContext and then using the data as a Binding in the XAML code to populate the GridView with all the Titles, Subtitles and Images that each Item requires.
In theory this should work, but in my trials and tests the page remains blank while all the C# code behind the scenes makes it seem like it should be populated.
I don't know why the page is not filling to I am turning to the collective help of all of you.
P.S: I don't really care about the neatness of this code, I just want to get it working.
XAML File
<Page
x:Name="pageRoot"
x:Class="Melbourne_Getaway.FavouritesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Melbourne_Getaway"
xmlns:data="using:Melbourne_Getaway.Data"
xmlns:common="using:Melbourne_Getaway.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<x:String x:Key="AppName">Favourites</x:String>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition />
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.RowSpan="2"
Padding="60,136,116,46"
SelectionMode="None"
IsSwipeEnabled="false"
CanReorderItems="True"
CanDragItems="True"
AllowDrop="True"
ItemsSource="{Binding Items}">
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="107">
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" />
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" Height="30" Margin="15,0,15,0" FontWeight="SemiBold" />
<TextBlock Text="{Binding Group}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" TextWrapping="NoWrap" Margin="15,-15,15,10" FontSize="12" />
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button" />
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" />
</Grid>
</Grid>
CS File
using Melbourne_Getaway.Common;
using Melbourne_Getaway.Data;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Windows.Storage;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace Melbourne_Getaway
{
public sealed partial class FavouritesPage : Page
{
public ObservableCollection<ItemData> Items { get; set; }
private ObservableDictionary defaultViewModel = new ObservableDictionary();
private NavigationHelper navigationHelper;
private RootObject jsonLines;
private StorageFile fileFavourites;
private Dictionary<string, ItemData> ItemData = new Dictionary<string, ItemData>();
public FavouritesPage()
{
loadJson();
getFavFile();
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
private void setupObservableCollection()
{
Items = new ObservableCollection<ItemData>(ItemData.Values);
DataContext = Items;
}
private async void loadJson()
{
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///DataModel/SampleData.json"));
string lines = await FileIO.ReadTextAsync(file);
jsonLines = JsonConvert.DeserializeObject<RootObject>(lines);
feedItems();
}
private async void getFavFile()
{
Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
fileFavourites = await storageFolder.GetFileAsync("MelbGetaway.fav");
}
private async void feedItems()
{
if (await FileIO.ReadTextAsync(fileFavourites) != "")
{
foreach (var line in await FileIO.ReadLinesAsync(fileFavourites))
{
foreach (var Group in jsonLines.Groups)
{
foreach (var Item in Group.Items)
{
if (Item.UniqueId == line)
{
var storage = new ItemData()
{
Title = Item.Title,
UniqueID = Item.UniqueId,
ImagePath = Item.ImagePath,
Group = Group.Title
};
ItemData.Add(storage.UniqueID, storage);
}
}
}
}
}
else
{//should only execute if favourites file is empty, first time use?
foreach (var Group in jsonLines.Groups)
{
foreach (var Item in Group.Items)
{
var storage = new ItemData()
{
Title = Item.Title,
UniqueID = Item.UniqueId,
ImagePath = Item.ImagePath,
Group = Group.Title
};
ItemData.Add(storage.UniqueID, storage);
await FileIO.AppendTextAsync(fileFavourites, Item.UniqueId + "\r\n");
}
}
}
setupObservableCollection();
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
#region NavigationHelper loader
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
private async void MessageBox(string Message)
{
MessageDialog dialog = new MessageDialog(Message);
await dialog.ShowAsync();
}
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
this.defaultViewModel["Groups"] = sampleDataGroups;
}
#endregion NavigationHelper loader
#region NavigationHelper registration
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
navigationHelper.OnNavigatedFrom(e);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
}
#endregion NavigationHelper registration
}
public class ItemData
{
public string UniqueID { get; set; }
public string Title { get; set; }
public string Group { get; set; }
public string ImagePath { get; set; }
}
}
Without a good Minimal, Complete, and Verifiable code example it is impossible to know for sure what's wrong. However, one glaring error does appear in your code:
private void setupObservableCollection()
{
Items = new ObservableCollection<ItemData>(ItemData.Values);
DataContext = Items;
}
In your XAML, you bind to {Binding Items}. With the DataContext set to the Items property value, the correct binding would actually be just {Binding}.
Alternatively, if you want to keep the XAML the way it is, you would have to set DataContext = this; instead. Of course, if you did it that way, then you would run into the problem that you don't appear to be raising INotifyPropertyChanged.PropertyChanged, or even implementing that interface. You can get away with that if you are sure the property will be set before the InitializeComponent() method is called, but in the code you've shown that does not appear to be the case.
So if you want to set the binding to {Binding Items} you also need to implement INotifyPropertyChanged and make sure you raise the PropertyChanged event with the property name "Items" when you actually set the property.
If the above does not address your question, please improve the question by providing a good MCVE that reliably reproduces the problem.
I figured it out. my problem lied in the way I was trying to pass the Data to the page itself. Instead of using DataContext = Items;and trying to access the data that way. I instead set the direct ItemsSource for the GridView.
The end result was simply changing DataContext = Items to itemGridView.ItemsSource = Items;

Data disappears from ListView after assigning new data to ItemsSource

I have the following ListView:
<ListView
Grid.Row="1"
ItemsSource="{Binding MeditationDiary}" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="{Binding ElementName=ListViewHeaders, Path=ActualWidth}" >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Text="{Binding StartTime}" />
<TextBlock
Grid.Column="1"
Text="{Binding TimeMeditated}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is bound to a MeditationDiary property of type MeditationDiary consisting of MeditationEntries:
public class MeditationDiary : Collection<MeditationEntry> { }
public class MeditationEntry
{
public DateTime StartTime { get; set; }
public TimeSpan TimeMeditated { get; set; }
}
The ListView binds to this MeditationDiary:
private MeditationDiary _meditationDiary;
public MeditationDiary MeditationDiary
{
get
{
RaisePropertyChanged(nameof(MeditationDiary));
return _meditationDiary;
}
set
{
_meditationDiary = value;
RaisePropertyChanged(nameof(MeditationDiary));
}
}
Strangely enough when I assign a new MeditationDiary object to the property (which contains data with MeditationEntries) the ListView does no longer display data.
I'm assigning the new MeditationDiary object in the UpdateDiary method which is called after adding an entry:
private async void UpdateDiary()
{
var latestDiary = await _repository.GetAsync();
MeditationDiary = latestDiary;
}
Why can this be and how can it be fixed?
This is most likely the culprit that's messing up your binding:
MeditationDiary = latestDiary;
Instead, try clearing out the current collection and then adding the new values to it:
MeditationDiary.Clear();
foreach (var entry in latestDiary)
MeditationDiary.Add(entry);
You'll probably have to call RaisePropertyChanged on the collection after you add the new items.
As a side note, you could replace MeditationDiary with an ObservableCollection<MeditationEntry>, which automatically notifies the UI when you add/remove items in it.

How do i bind ListView Data with List using MVVM

I'm trying to bind List<> to ListView. When i'm updating i'm going to clear the list. Clear on ObservableCollection was kind of slow.
Problem is that in the view things are not updated correctly.
XAML
<StackPanel.Resources>
<ResourceDictionary>
<common:BoolToBackgroundConverter x:Key="BoolToBackground"/>
<tb:StringInlineCollectionConvertor x:Key="InlineConvert"/>
</ResourceDictionary>
</StackPanel.Resources>
<ListView ItemsSource="{Binding NotificationsCollection, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
ScrollViewer.CanContentScroll="False">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<Grid Background="{Binding NotSeen,Converter={StaticResource BoolToBackground},UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Thumb}"/>
<tb:BindableTextBlock InlineCollection="{Binding Text, Converter={StaticResource InlineConvert}}"/>
<TextBlock Text="{Binding Created}"/>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#
ViewModel
public List<NotificationDataModel> notificationsCollection;
public List<NotificationDataModel> NotificationsCollection
{
get
{
if (notificationsCollection == null)
{
notificationsCollection = new List<NotificationDataModel>();
}
return notificationsCollection;
}
set
{
if (notificationsCollection == null)
{
notificationsCollection = new List<NotificationDataModel>();
}
notificationsCollection.Clear();
foreach (var item in value)
{
notificationsCollection.Add(item);
}
this.OnPropertyChanged("NotificationsCollection");
}
}
public void UpdateNotifications1()
{
List<NotificationDataModel> newCollection = new List<NotificationDataModel>();
newCollection.Add(item1);
newCollection.Add(item2);
newCollection.Add(item3);
newCollection.Add(item4);
newCollection.Add(item5);
newCollection.Add(item6);
this.NotificationsCollection = newCollection;
}
public void UpdateNotifications2()
{
List<NotificationDataModel> newCollection = new List<NotificationDataModel>();
newCollection.Add(item1);
newCollection.Add(item2);
newCollection.Add(item6);
this.NotificationsCollection = newCollection;
}
When i call UpdateNotifications1 elements are show accordingly but after that when i call UpdateNotifications2 i see item1, item2 and item3 instead of item6.
Also items are getting new value for the NotSeen(black for example, and initial is white) property after closing the view and on reopening those items should have white but they are still with black background.
The problem is that raising the PropertyChanged event in your NotificationsCollection setter is ineffective when the actual value of the underlying field hasn't changed. The target ItemsSource property receives the same List instance (from the binding) and therefore does not trigger a UI update.
So do not clear and copy to an existing collection, but use the one passed to the setter instead:
public List<NotificationDataModel> NotificationsCollection
{
get { return notificationsCollection; }
set
{
notificationsCollection = value;
OnPropertyChanged("NotificationsCollection");
}
}
well either create a new list every time or use something like ObservableCollection<>

Best approach to add an item to a databound Listbox using MVVM pattern?

I'm facing a problem in my WPF project at the moment. At this moment I have a Viewmodel which has a Manager (to communicate with the repo).
internal class TicketViewModel
{
private TicketManager mgr;
public IEnumerable<Ticket> List { get; set; }
public TicketViewModel()
{
mgr = new TicketManager();
List = mgr.GetTickets();
}
}
I've managed to bind this list to the Listbox in my MainWindow. The next step is that I need to add an extra ticket to the list and also pass this through the manager. The problem is I need two parameters from some Controls in the MainWindow. From MVVM perspective I need to use bound Commands on e.g. a Button to communicate with the viewmodel as my viewmodel can't/may not access controls from the window. Is using parameterized Commands the way to go here?
The next problem is that the Listbox won't update I guess. This is the code:
<ListBox x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I found that using a CompareableCollection is the way to go here, but then I still have to read all the Tickets again after adding a new Ticket.
Thanks in advance,
Hicy
okay here is the code.
Lets say you have three textboxes on MainWindow(since you have three Textblocks.) so Your MainWindow.xaml looks like
<Window.DataContext>
<local:MyViewModel/>--set's your viewModel
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="250*"/>
<RowDefinition Height="90"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="TicketNumber" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=Text}" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="Text" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=State}" />
<TextBox x:Name="State" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=TicketNumber}" />
<Button Content="Button" Command="{Binding Path=MainCommand}" Grid.Row="2"/>
</Grid>
and I am assuming that you have some class called class Ticket which contain these three members
Class Ticket
{
public int TicketNumber { get; set; }
public string Text { get; set; }
public string State { get; set; }
}
Now in class TicketManager we fill it with some dummy data
class TicketManager
{
ObservableCollection<Ticket> tl = new ObservableCollection<Ticket>();
internal ObservableCollection<Ticket> GetTickets()
{
tl.Add(new Ticket() { State = "State1", Text = "Text1", TicketNumber = 1 });
tl.Add(new Ticket() { State = "State2", Text = "Text2", TicketNumber = 2 });
tl.Add(new Ticket() { State = "State3", Text = "Text3", TicketNumber = 3 });
return tl;
}
}
and in your Mainwindow ViewModel lets call it MyViewModel.cs we add
class MyViewModel:INotifyPropertyChanged
{
private TicketManager mgr;
public ObservableCollection<Ticket> List { get; set; }
private string text;
private string state;
private int ticketNumber;
private readonly DelegateCommand<object> MyButtonCommand;
public Class1()
{
mgr = new TicketManager();
List = mgr.GetTickets();
MyButtonCommand = new DelegateCommand<object>((s) => { AddListToGrid(text, state, ticketNumber); }, (s) => { return !string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(state); });
}
private void AddListToGrid(string text, string state, int ticketNumber)
{
List.Add(new Ticket() {Text=text,State=state,TicketNumber=ticketNumber });
}
public DelegateCommand<object> MainCommand
{
get
{
return MyButtonCommand;
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged("State");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public int TicketNumber
{
get
{
return ticketNumber;
}
set
{
ticketNumber = value;
OnPropertyChanged("TicketNumber");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can Modify the code in anyway you want
This ViewModel implements fewthings which are very important from MVVM point of view
1) INotifyPropertyChanged
2) WPF Delegate Command
P.S:The code is tested and it runs as expected
Don't get hung up on MVVM it is simply a separation of data from a view, and models are shared between the two with a majority of the business logic (on a shared component) should be performed on the VM; it is not a religion just a three tiered data system. IMHO
If your button needs to do an operation, have it make a call, most likely in the code behind, to a method on the VM which handles the business logic, updates the list with the new item and notifies the manager.
I would bind the list in question to an ObservableCollection which can notify upon insert/delete of an item.

Categories

Resources