Reputation: 2490
I have a simple View and ViewModel. When you click on the ComboBox of Technicians and select a Technician, the Tests for the SelectedTechnician are displayed in the ListView you see below. This works perfectly well.
Note Technician has a one to many relationship with Test and each is an entity (Code-First Entity Framework).
MainViewModel.xaml
<Grid>
<StackPanel>
<ComboBox ItemsSource="{Binding Technicians, Mode=TwoWay}"
DisplayMemberPath="FullName"
SelectedItem="{Binding SelectedTechnician, Mode=TwoWay}">
</ComboBox>
<ListView ItemsSource="{Binding SelectedTechnician.Tests}"
SelectedItem="{Binding SelectedTest}"
HorizontalAlignment="Left" Height="100" Margin="10,10,0,0" VerticalAlignment="Top" Width="271">
<ListView.View>
<GridView>
<GridViewColumn Header="Id" Width="auto" DisplayMemberBinding="{Binding Id}"/>
<GridViewColumn Header="Test" Width="auto" DisplayMemberBinding="{Binding TestTypeName}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>
MainWindowViewModel.cs
public class MainWindowViewModel : ViewModelBase
{
private WpfTestBedDb db = new WpfTestBedDb();
public MainWindowViewModel()
{
Technicians = new ObservableCollection<Technician>();
}
private ObservableCollection<Technician> _technicians;
public ObservableCollection<Technician> Technicians
{
get { return _technicians; }
set
{
_technicians = value;
OnPropertyChanged("Technicians");
}
}
private Technician _selectedTechnician;
public Technician SelectedTechnician
{
get { return _selectedTechnician; }
set
{
if (_selectedTechnician != value)
{
_selectedTechnician = value;
OnPropertyChanged("SelectedTechnician");
}
}
}
public void LoadTechnicians()
{
var query = from tech in db.Technicians
select tech;
foreach (var technician in query)
{
Technicians.Add(technician);
}
}
}
Incidentally, to test out the capabilities of DataBinding on the ObservableCollection<Technician> Technicians;
, as you see in the ViewModel above, I went into the database through SSMS and added a new Technician WHILE the app was still running. Once my addition had been made, I went to the app and clicked on my ComboBox of Technicians only to find that the list/UI hadn't been updated. And yet, I would have thought that because I had made the property Technicians an ObservableCollection, and one that raises the OnPropertyChanged("Technicians");
, that the UI would have updated.
I did this experiment to simulate two of the same apps running at the same time, each reading and writing to the same database.
First Question: Why isn't the UI updating despite having my ObservableCollection raise OnPropertyChanged? That is, why isn't it aware of the changes made directly to the database (outside the app)?
Second Question: What do I need to add so that the collection of Technicians is constantly aware of DB changes? (I thought that is what ObservableCollection was for)
Upvotes: 1
Views: 138
Reputation: 11326
In addition to Marc Gravell's answer, and in answer to your second question, you might like to check out the SqlDependency
class.
Quoting from the documentation:
The SqlDependency object represents a query notification dependency between an application and an instance of SQL Server. An application can create a SqlDependency object and register to receive notifications via the OnChangeEventHandler event handler.
Upvotes: 2
Reputation: 1063864
Short version: most data access code is a disconnected model, not a live/connected model.
The "observable" here means "reacts to other changes in that layer", i.e. other .NET code manipulating the object model. Using ObservableCollection<T>
doesn't make a giant hand reach out over the network from the database to tweak the bits.
Making instantly change-aware systems is more complicated than that; frankly I would suggest looking at something like pub/sub if you genuinely need instant updates. Otherwise, perhaps consider polling occasionally.
Additionally, note that if you do want to do polling, you can't just re-fetch the object via:
var stateNow = db.Technicians.Single(x => x.Id == myId);
because that might just hand you back the same object you already have; a lot of ORMs have an identity cache built in. To actually re-fetch the object you will need a second db-context instance.
Upvotes: 3