CollectionViewSource Not Updating with PropertyChanged - c#

I'm having an enormous amount of trouble with ComboBoxes in a data grid.
And really would like some help, i think i've gotten confused by the amount of research and things i've tried. This really should be simple so i must be missing something.
SIMPLIFIED PROBLEM
I use a CollectionViewSource in xaml, the C# sets the source of that CollectionViewSource to an ObservableCollection in a class that is the Page's datacontext.
Adding items to the collection does not update the DataGridComboBox column containing that displays the view source.
See below the line for more detail
OVERVIEW
I have a WPF Page with a datagrid on it.
The page has its data context set to a view model.
The viewModel contains two observable collections. One for Equips and One for Locations.
Each Equip has a Location.
These are populated from a code first EF database but i believe that this problem is above that level.
The datagrid is one row per Equip. The Location column needs to be a selectable combobox that allows the user to change Location.
The only way i could get the location combobox to populate at all is by binding it to a separate collection view source.
PROBLEM
It seems that if the Page loaded event occurs prior to the ViewModel populating the ObservableCollection then the locationVwSrc will be empty and the property changed event doesn't get this to change.
IMPLEMENTATION SHORT VERSION
Page has a collection viewSource defined in the xaml.
Loaded="Page_Loaded"
Title="EquipRegPage">
<Page.Resources>
<CollectionViewSource x:Key="locationsVwSrc"/>
</Page.Resources>
The datagrid is defined with xaml.
<DataGrid x:Name="equipsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" Margin="10,10,-118,59"
ItemsSource="{Binding Equips}" EnableRowVirtualization="True" AutoGenerateColumns="False">
The combobox column defined in xaml
<DataGridComboBoxColumn x:Name="locationColumn" Width="Auto" MaxWidth="200" Header="Location"
ItemsSource="{Binding Source={StaticResource locationsVwSrc}, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
SelectedValueBinding="{Binding Location}"
The page context set to the view model
public partial class EquipRegPage : Page
{
EquipRegVm viewModel = new EquipRegVm();
public EquipRegPage()
{
InitializeComponent();
this.DataContext = viewModel;
}
Loaded event setting the context
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Locations View Source
System.Windows.Data.CollectionViewSource locationViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("locationsVwSrc")));
locationViewSource.Source = viewModel.Locations;
// Above does not work if the viewmodel populates these after this call, only works if its populated prior.
//TODO inotifypropertychanged not correct? This occurs before the viewmodels loads, and doesn't display.
// Therefore notify property changes aren't working.
// Using this as cheat instead instead works, i beleive due to this only setting the source when its full
//viewModel.Db.Locations.Load();
//locationViewSource.Source = viewModel.Db.Locations.Local;
//locationViewSource.View.Refresh();
}
The ViewModel class and how it loads
public class EquipRegVm : DbWrap, INotifyPropertyChanged
{
/// <summary>
/// Event triggered by changes to properties. This notifys the WPF UI above which then
/// makes a binding to the UI.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notify Property Changed Event Trigger
/// </summary>
/// <param name="propertyName">Name of the property changed. Must match the binding path of the XAML.</param>
void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<Equip> Equips { get; set; }
public ObservableCollection<Location> Locations { get; set; }
public EquipRegVm() : base()
{
Load();
}
/// <summary>
/// Load the data from the Model.
/// </summary>
public async void Load() //TODO async an issue?
{
// EQUIPMENT
ObservableCollection<Equip> eqList = new ObservableCollection<Equip>();
var eqs = await (from eq in Db.Equips
orderby eq.Tag
select eq).ToListAsync();
foreach(var eq in eqs)
{
eqList.Add(eq);
}
Equips = eqList;
RaisePropertyChanged("Equips");
// LOCATIONS
ObservableCollection<Location> locList = new ObservableCollection<Location>();
var locs = await (from l in Db.Locations
orderby l.Name
select l).ToListAsync();
foreach (var l in locs)
{
locList.Add(l);
}
Locations = locList;
RaisePropertyChanged("Locations");
}
}

It seems you haven't been able to break the problem down into small enough problems. The question seems to be a mix of ComboBoxes in Datagrid, Asynchronously setting CollectionViewSource source, loading data from a database.
I suggest that it would be beneficial to either consider
recreating the problem (or soultion) with the minimum moving parts i.e. a XAML file and a ViewModel with pre canned data.
or decoupling your existing code. It appears that Page knows about your ViewModel explicitly (EquipRegVm viewModel = new EquipRegVm();), and you ViewModel knows explicitly about Databases and how to load itself. Oh snap, now our views are coupled to your database? Isn't that the point of patterns like MVVM so that we are not coupled?
Next I look at some the code and see some more (of what I would call) anti-patterns.
Settable collection properties
code behind for the page (all could live in the XAML)
But I think basically if you just changed your code in 3 places you should be fine.
Change 1
/*foreach(var eq in eqs)
{
eqList.Add(eq);
}
Equips = eqList;
RaisePropertyChanged("Equips");*/
foreach(var eq in eqs)
{
Equips.Add(eq);
}
Change 2
/*foreach (var l in locs)
{
locList.Add(l);
}
Locations = locList;
RaisePropertyChanged("Locations");*/
foreach (var l in locs)
{
Locations.Add(l);
}
Change 3
Either just remove the usage of the CollectionViewSource (what does it offer you?) or use binding to set the source. As you are currently manually setting the Source (i.e. locationViewSource.Source = viewModel.Locations;) you have opted out of getting that value updated when the PropertyChanged event has been raised.
So if you just delete the CollectionViewSource, then you just have to bind to the Locations property. If you decide to keep the CollectionViewSource then I would suggest deleting the page codebhind and just changing the XAML to
<CollectionViewSource x:Key="locationsVwSrc" Source="{Binding Locations}" />

Set a Binding like below :
System.Windows.Data.CollectionViewSource locationViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("locationsVwSrc")));
// locationViewSource.Source = viewModel.Locations;
Binding b = new Binding("Locations");
b.Source = viewModel;
b.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(locationViewSource, CollectionViewSource.SourceProperty, b);
This is all you need.

Related

Calling multiple OnPropertyChanged within the same Setter (or is there a other easy way)

I am trying to keep my question simple and to the point.
At the moment, if I have a property that updates the underlying Model data, and it therefore needs to inform a few other properties that the source has changed, I do it like this:
public Data.MeetingInfo.Meeting Meeting
{
get { return _Meeting; }
set
{
if(value != null)
{
_Meeting = value;
if (_Meeting.IsDirty)
{
_Model.Serialize();
_Meeting.MarkClean();
OnPropertyChanged("Meeting");
OnPropertyChanged("BibleReadingMain");
OnPropertyChanged("BibleReadingClass1");
OnPropertyChanged("BibleReadingClass2");
}
}
}
}
private Data.MeetingInfo.Meeting _Meeting;
As you can see, I added several different OnPropertyChanged method calls. Is this an acceptable way to do it? Or, can the specific properties in the Model inform the View that some of it's source has changed?
I have read about implementing the same OnPropertyChanged features in the Model classes. Thus the XAML will pick it up. But I thought those two parts of the MWWV we not supposed ot know about each other.
The thing is, the other 3 are in disabled controls, but they can be updated from two places on the window. So I don't think I can have two update source triggers can I?
Thank you.
Second attempt at explainign things:
ObservableCollection of Meeting objects. Bound to a ComboBox:
<ComboBox x:Name="comboMeetingWeek" ItemsSource="{Binding Meetings}"
SelectedItem="{Binding Meeting, UpdateSourceTrigger=PropertyChanged}" />
The Meeting object contains several properties. We bind controls on the window with these properties. Example:
<ComboBox x:Name="comboNotes" IsEditable="True"
DataContext="{Binding Meeting}"
Text="{Binding Note, UpdateSourceTrigger=LostFocus}"
ItemsSource="{StaticResource Notes}"/>
I do this for the majority of the controls. So the Meeting property in the view model is kept up to date and then when you select a different meeting it commits it to the model data and displays the new meeting (as previously described).
But, in some places on the window, I have some disabled text boxes. These are associated with properties nested inside the Meeting object. For example:
<TextBox x:Name="textBibleReadingMain" Grid.Column="0" Margin="2" IsEnabled="False"
DataContext="{Binding TFGW.BibleReadingItem.Main}"
Text="{Binding DataContext.BibleReadingMain, ElementName=oclmEditor, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"/>
The parent TabItem already has it's DataContext set to {Binding Meeting}. What we need to display in the text box is:
Meeting (current context).TFGW.BibleReadingItem.Main.Name
This is why I had to do it has I did. For the above text box, this is what I want to allow to happen:
It should display the content of Meeting.TFGW.BibleReadingItem.Main.Name (Meeting already being a bound property).
As you select a different meeting from the dates combo, this text box should update.
If the user selects a name from the DataGrid and the ActiveAstudentAssignmentType combo is set to StudentAssignmentType::BibleReadingMain then I also want to update the text box.
I think what I am getting confused about is when I am supposed to derive my classes from INotifyPropertyChanged. My Model data is the Meeting objects with it's own data. Should all of these be inheriting from INotifyPropertyChanged and raising OnPropertyChanged? At the moment I do not have that implemented anywhere. I tell a lie, the only place I implemented it was for the view model itself:
public class OCLMEditorViewModel : INotifyPropertyChanged
So that is why I had to do it the way I did.
Any clearer?
Based on all the comments and further reasearch ....
One of the answers stated:
Viewmodel is created and wraps model
Viewmodel subscribes to model's PropertyChanged event
Viewmodel is set as view's DataContext, properties are bound etc
View triggers action on viewmodel
Viewmodel calls method on model
Model updates itself
Viewmodel handles model's PropertyChanged and raises its own PropertyChanged in response
View reflects the changes in its bindings, closing the feedback loop
I also read a bit of this (which confused me somewhat) where it stated:
The Model notifies the ViewModel if the data in the underlying data store has changed.
So, the first thing I did was change my Meeting object to derive from INotifyPropertyChanged. In addition, I added new properties for gaining access to deeper data in the Meeting model. Example (stripped down):
public class Meeting : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
#region Bible Reading Name Properties
[XmlIgnore]
public string BibleReadingMainName
{
get { return _TFGW.BibleReadingItem.Main.Name; }
set
{
_TFGW.BibleReadingItem.Main.Name = value;
OnPropertyChanged("BibleReadingMainName");
}
}
[XmlIgnore]
public string BibleReadingClass1Name
{
get { return _TFGW.BibleReadingItem.Class1.Name; }
set
{
_TFGW.BibleReadingItem.Class1.Name = value;
OnPropertyChanged("BibleReadingClass1Name");
}
}
[XmlIgnore]
public string BibleReadingClass2Name
{
get { return _TFGW.BibleReadingItem.Class2.Name; }
set
{
_TFGW.BibleReadingItem.Class2.Name = value;
OnPropertyChanged("BibleReadingClass2Name");
}
}
#endregion
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
In my ViewModel I set it as a listener for PropertyChanged:
_Meeting.PropertyChanged += Meeting_PropertyChanged;
At this point in time, the handler just relays the property that was changed:
private void Meeting_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged(e.PropertyName);
}
In my XAML, I adjust my TextBox to work with the new property, and I remove the DataContext reference. So I now have:
<TextBox x:Name="textBibleReadingMain" Grid.Column="0" Margin="2" IsEnabled="False"
Text="{Binding BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
ON the right, where I have the DataGrid, when we click a row and the SelectedStudentItem is updated, we can now do:
private Student _SelectedStudentItem;
public Student SelectedStudentItem
{
get
{
return _SelectedStudentItem;
}
set
{
// We need to remove this item from the previous student history
if (_SelectedStudentItem != null)
_SelectedStudentItem.History.Remove(Meeting.DateMeeting);
_SelectedStudentItem = value;
if (_SelectedStudentItem == null)
return;
_EditStudentButtonClickCommand.RaiseCanExecuteChanged();
_DeleteStudentButtonClickCommand.RaiseCanExecuteChanged();
OnPropertyChanged("SelectedStudentItem");
if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingMain)
_Meeting.BibleReadingMainName = _SelectedStudentItem.Name;
else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass1)
_Meeting.BibleReadingClass1Name = _SelectedStudentItem.Name;
else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass2)
_Meeting.BibleReadingClass2Name = _SelectedStudentItem.Name;
}
Based on the current ActiveStudentAssignmentType value we can directly update the source property. Thus the TextBox will automatically know about it due to the PropertyChange listener.
Thus, the original Meeting property code now looks like this:
public Data.MeetingInfo.Meeting Meeting
{
get { return _Meeting; }
set
{
// Has the existing meeting object changed at all?
if(_Meeting != null && _Meeting.IsDirty)
{
// Yes, so save it
_Model.Serialize();
_Meeting.MarkClean();
}
// Now we can update to new value
if (value != null)
{
_Meeting = value;
OnPropertyChanged("Meeting");
}
}
}
private Data.MeetingInfo.Meeting _Meeting;
All of those extra OnPropertyChanged calls are now obsolete!
The thing I was missing was implementing Notification from the Model to the ViewModel. And then the ViewModel informing the View.

ListView does not update when source is changed - Even though I implemented PropertyChanged

I've been struggling with this problem for hours now:
I'm building an UWP application in the MVVM pattern.
Problem: I can't get my ListView's ItemSource to update/rebind to a new instance of ObservableCollection, when I change the ItemSource's property's value, even though I have implemented IPropertyChanged on the property.
Details:
The page I am working on has a ListView which ItemSource is bound to an observable collection:
Page:
<ListView ItemsSource="{Binding Orders}" x:Name="OrderListView"><!--Inner bindings works fine--></ListView>
ViewModel:
public ObservableCollection<Order> Orders {
get { return _Orders; }
set { Set(ref _Orders, value); }
}
private ObservableCollection<Order> _Orders;
(...)
//Property changed implementation from video (06:48): https://mva.microsoft.com/en-US/training-courses/windows-10-data-binding-14579?l=O5mda3EsB_1405632527
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
//If property is updated with, raise property changed, else don't
public bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
return false;
storage = value;
RaisePropertyChanged(propertyName);
return true;
}
When pressing a button in the UI:
<Button Content="Waiter" Click="Waiter_Click" Background="{Binding Waiter, Converter={StaticResource BoolToColorConverter}, FallbackValue='Yellow', TargetNullValue='Red'}"/>
It will tricker the "Waiter_Click" event handler in the code behind:
private void Waiter_Click(object sender, RoutedEventArgs e)
{
var viewModel = ((Button)sender).DataContext as OrderPageViewModel;
if (viewModel.Waiter) viewModel.Waiter = false;
else viewModel.Waiter = true;
}
The Waiter property is implemented, so it raises PropertyChangedEvent in the ViewModel:
public bool Waiter
{
get { return _waiter; }
set { Set(ref _waiter, value); FilterView(); }
}
private bool _waiter;
The button is updated in the UI - This works fine.
After setting the private field of _waiter, I also want to filter out some data that I don't want to show.
I do this in the FilterView(); method.
This is done by creating a new ObservableCollection, and setting the Orders property:
public void FilterView()
{
var filteredOrderList = new ObservableCollection<Order>();
//Sorting is done
Orders = filteredOrderList;
}
Now. I thought that the code: Orders = filteredOrderList; would trigger the "setter" of Orders property, and raise the PropertyChanged event as it does with the button and all other UI elements that works fine - and tell the ListView to rebind to it. However it seems like the ListView is running on an old instance of the ObservableCollection, and does not care about my PropertyChanged event being fired...
I've debugged the program, and I see that filteredOrderList contains the data I want, and that Orders is changed. But it's like ViewList does not care about the PropertyChanged event is fired...
I don't want to add and remove items from the ObservableCollection because the filtering becomes very complicated... I just want to replace the Orders ObservableCollection with a new one, and let the ViewList reflect my changes...
Am I missing something? Can't I call PropertyChanged event multiple times? (I'm calling it once for the button update, and once for the Orders ObservableCollection to be changed).
Why is ListView not rebinding to the the new list? -Does ListView not care about PropertyChanged events being fired?
I will deeply appreciate any answer or workaround that will fix this or send me in the right direction.
Thank you, and sorry for long post.
Have a look at these two:
Sort ObservableCollection C#
Common mistakes while using ObservableCollection
After 8 and half an hour I found my mistake:
I had accidentally defined the DataContext two times:
<Page.DataContext>
<vm:OrderPageViewModel/>
</Page.DataContext>
and further down the XAML file just before the ListView:
<Grid.DataContext>
<vm:OrderPageViewModel/>
</Grid.DataContext>
This caused two instances of my OrderPageViewModel.
My ViewList was then bound to the old instance of the ViewModel, and any changes to the new ViewModel instance of ObservableCollection did not reflect any changes.
If someone else get's the same problem: That ViewList does not update when changing ObservableCollection - It's properbly because you created multiple instances of the ObservableCollection.
Also I found out that ObservableCollection only updates when adding og removing objects from it and clearing it using .Add(), .Remove() and .Clear() methods. It is not posible to call "CollectionChanged" or "PropertyChanged" without implementing it in the property itself, as I did on the Waiter property.
I remade the filtering function to clear the collection and add new elements to it. It fixed my issue. :-)
3 lines in XAML can really mess up your day, if you are not carefull.
Have a great day, and thank you people who posted previous answers :-)

Populate Observable Collection from LINQ query results

I'm just starting with WPF MVVM & I'm trying to populate a datagrid view by binding to an ObservableCollection in my view model.
I have used a simple LINQ query to select all from my table and populate my observable collection but my UI won't display the grid results, presumably because the XAML has to bind to a public property?
private void getTableResults()
{
var query = from c in LDB.myTable
select c;
var results = query;
ObservableCollection<myTable> MyCollection = new ObservableCollection<myTable>(results);
OnPropertyChanged("MyCollection");
}
Here's my XAML binding:
<DataGrid ItemsSource="{Binding MyCollection}"
My DataContext is set and I've checked it binds against another hard-coded property. Where am I going wrong? I've put a breakpoint over the collection so know that it is being populated by the results. How can I expose a public ObservableCollection property which is populated from my LINQ results?
Thanks
Just to add, my data model is MyTable which is an Entity Framework 4.0 edmx.
Assuming the data context of your view is your viewmodel, then yes - your xaml binding should be to a public property of your viewmodel.
So declare your 'MyCollection' property as a property of the viewmodel (not a local property of your getTbaleResults method).
private ObservableCollection<myTable> _myCollection;
/// <summary>
/// Gets or sets the myTable collection.
/// </summary>
public ObservableCollection<myTable> MyCollection
{
get { return _myCollection; }
set
{
if (value == _myCollection) return;
_myCollection= value;
RaisePropertyChanged(() => MyCollection);
}
}
(Note this is using Prism so the lambda on RaisePropertyChanged, but other frameworks have similar implementations).

My dataGrid doesn't clear and doesn't refresh

I have two DataGrid, each binding in a dataSource like this :
ItemsSource="{Binding Data, ElementName=EmpSource, Mode=TwoWay}"
The first DataGrid(dgJob), contains Job and the second(dgEmp), the employee linked to the job.
I want to keep all the employees in the EmpSource, and display in the dataGrid, only those who are linked to the selected job in my first datagrid.
So I am doing this in the dgJob selectionChanged event :
dgEmp.ItemsSource = null;
var lstEmp = EmpSource.DataView.OfType<Emp>().Where(ores => ores.IdJob == itmJobSelect.IdJob).ToList();
dgEmp.ItemsSource = lstEmp;
The problem is, the dataGrid is not clearing when I change the selected line in my datagrid with the jobs, so for every job, I display every Employees in the dgEmp, while I should only display those who are connected to the job.
I can delete the line in the xaml, that determine the dataSource, but if I do this, I must refresh the dataGrid when there is a change in the dataSource.
But I don't found how to refresh it(at least for the first time) unless I write the 3 lines each time after a change in dataSource.
Can somebody help me find a solution to my problem?
Thank you.
I recommend you to use MVVM design pattern. You should load your data in view model class and store it in collection which implements INotifyCollectionChanged interface. View model should also implement INotifyPropertyChanged interface.
When your employee collection changes, you should filter second collection as in following code:
Jobs.CollectionChanged += (sender, args) =>
{
Employees = AllEmployees.Where(c=> c.IdJob == SelectedJob.IdJob);
}
You should also do same thing when SelectedJob changes and DataGrid will be refreshed.
This will work only when you will have implemented property changed notifications and correct binding was specified.
Here's example of property changed implementation which you should write:
public class ViewModel : INotifyPropertyChanged
{
public IEnumerable<Emp> Employees
{
get { return _employees; }
set
{
if (_employees != value)
{
_employees = value;
OnPropertyChanged("Employees");
}
}
}
/* ... */
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You should also assign your view model instance to DataContext for make binding works. For example in code behind file constructor:
public void Page()
{
DataContext = new ViewModel();
InitializeComponent();
}

MVVM DataBinding

I've started an MVVM project and now I'm stucking with correct DataBinding.
My project has:
A UserControl whit a ViewModel as DataContext like:
public partial class TestUserControl: UserControl
{
public TestUserControl()
{
this.DataContext = new TestUserControlViewModel();
}
}
ViewModel code is (BaseViewModel class contains PropertyChangedEventHandler):
public class TestUserControlViewModel : BaseViewModel
{
public KrankenkasseControlViewModel()
{}
public IEnumerable<DataItem> GetAllData
{
get
{
IGetTheData src= new DataRepository();
return src.GetData();
}
}
}
IGetTheData is the interface to DataContext:
public interface IGetTheData
{
IEnumerable<DataItem> GetData();
}
}
and finally the DataRepository code:
public class DataRepository : IGetTheData
{
private TestProjectDataContext dax = new TestProjectDataContext();
public IEnumerable<DataItem> GetData()
{
return (from d in this.dax.TestData
select new DataItem
{
ID = d.ID,
SomeOtherData = d.SomeOtherData
});
}
}
My UserControl has a few TextBoxes, but what's the best way to bind correctly?
Thanks for your help, regards.
EDIT: Binding the data against multiple textboxes
After reading your comment, I will elaborate my example for textboxes.
First important thing is that the ViewModel will model the things in the View, so that the View gets all information it needs in the structure it needs. That means, if you have multiple textboses in the View, you will need multiple string Properties in your ViewModel, one for each textbox.
In your XAML you could have something like
<TextBox Text="{Binding ID, Mode=TwoWay}" />
<TextBox Text="{Binding SomeOtherData, Mode=TwoWay}" />
and in your ViewModel
public class TestUserControlViewModel : BaseViewModel {
private string id;
private string someOtherData;
public TestUserControlViewModel() {
DataItem firstItem = new DataRepository().GetData().First();
this.ID = firstItem.ID;
this.SomeOtherData = firstItem.SomeOtherData;
}
public string ID {
get {
return this.id;
}
set {
if (this.id == value) return;
this.id = value;
this.OnPropertyChangedEvent("ID");
}
}
public string SomeOtherData {
get {
return this.someOtherData;
}
set {
if (this.someOtherData == value) return;
this.someOtherData = value;
this.OnPropertyChangedEvent("SomeOtherData");
}
}
}
Here I assume that in your BaseViewModel there is an OnPropertyChangedEvent method to fire the corresponding event. This tells the View that the property has changed and it must update itself.
Note the Mode=TwoWay in the XAML. This means, that it doesn't matter on which side the value changes, the other side will reflect the change immediately. So if the user changes a value in a TwoWay bound TextBox, then the corresponding ViewModel property will automatically change! And also vice versa: if you change the ViewModel property programmatically, the View will refresh.
If you want to show multiple textboxes for more than one data item, then you must introduce more Properties in the ViewModel and bind them accordingly. Maybe a ListBox with a flexible number of TextBoxes inside is a solution then, like #Haspemulator already answered.
Binding the data against a collection control
In the TestUserControl I guess you have a control (like a ListView) to show the list of loaded things. So bind that control against the list in the ViewModel with
<ListView ... ItemsSource="{Binding GetAllData}" ... />
First you must understand that Binding means not "read the data and then forget the ViewModel". Instead you bind the View to the ViewModel (and its Properties) as long as the View lasts. From this point of view, AllData is a much better name than GetAllData (thanks #Malcolm O'Hare).
Now in your code, every time the View reads the AllData property, a new DataRepository is created. Because of the Binding, that is not what you want, instead you want to have one instance of DataRepository for the whole lifetime of the View, which is used to read the initial data and can later be used to update the View, if the underlying database changes (maybe with an event).
To enable such a behavior you should change the type of the AllData property to an ObservableCollection, so that the View can automatically update the list if changes occur.
public class TestUserControlViewModel : BaseViewModel
private ObservableCollection<DataItem> allData;
public TestUserControlViewModel() {
IGetTheData src = new DataRepository();
this.allData = new ObservableCollection<DataItem>(src.GetData());
}
public ObservableCollection<DataItem> AllData {
get {
return this.allData;
}
}
public void AddDataItem(DataItem item) {
this.allData.Add(item);
}
}
Now if you call AddDataItem later, the ListView will update itself automatically.
Your Property Name is bad. You should call it AllData, not GetAllData.
Since you are returning a collection, you probably should be using some sort of list control (ListBox, ListView).
In that case you'd be doing
<ListBox ItemsSource="{Binding GetAllData}" />
Guten Abend. :) As it already mentioned, since you're returning the collection, it's better to use a ListBox. The comment about having ObservableCollection as a cache is also absolutely valid. I would add that if you need to have your data editable, you should use TextBox inside the ItemTemplate:
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text={Binding SomeOtherData,Mode=TwoWay} />
</DataTemplate>
</ListBox.ItemTemplate>
In this case if user edits the text in the box, data will be updated in your data object, so that it could be saved in the database later.

Categories

Resources