INotifyCollectionChanged UI not updated - c#

I have problem with updating UI. I have class which, is used to binding my UI elements:
public class engine : INotifyCollectionChanged
{
RWProject project = new RWProject();
public ObservableCollection<string> ProjectListBinding
{
get { return project.list(); }
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void OnCollectionChanged(NotifyCollectionChangedEventArgs eventArgs)
{
if (this.CollectionChanged != null)
{
this.CollectionChanged(this, eventArgs);
}
}
private ICommand _usunProjekt;
public ICommand UsunProjekt
{
get
{
_usunProjekt = new UsunProjektCommand();
return _usunProjekt;
}
}
private ICommand _dodajProjekt;
public ICommand DodajProjekt
{
get
{
_dodajProjekt = new DodajNowyProjektCommand();
return _dodajProjekt;
}
}
}
ProjectListBinding is a list of files names inside folder, and this names are displayed on listview control.
Commands DodajProjekt creating in same folder, new file (UsunProjekt - removing)
Commands are binded to buttons.
I need to rise event
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
to update UI after command is executed, but I don't knew where to attach this part of code. Or maybe I should do it in different way ?
With code I already have, bindings and commands are working fine, only updating not working at all.
Can You help me solve this ?
Piotr

You should not be implementing INotifyCollectionChanged. From your view you need to bind to ProjectListBinding which will automatically raise INotifyCollectionChanged for you and update the UI. Of course, assuming that the class that you have (i.e. engine) is your view model.
When running your command, you should update ProjectListBinding for INotifyCollectionChanged events to be risen. That is, both your commands DodajNowyProjektCommand and UsunProjekt should be operating on ProjectListBinding.

Related

How To Create MessageBox or Dialog Box in design MVVM pattern application

To those who are MVVM Purist, my question is, Is there a more simplistic, user readable,and unit testable code solution to the problem "How to create message box or dialog box in MVVM design pattern application" then what I come up with here? Disclaimer, I'm not a MVVM Purist and I will add a few lines of code in the View's code-behind if it means more simplistic, user readable and unit testable code. My solution builds upon what awardcoder.blogspot suggested. The first thing on notice in the solution is there is View's code-behind for handling MessageBox. From this point, I realize the fact that adding code in the View's code-behind is already heading down a not MVVM Purist path. Therefore, my solution take full advantage of this single rule breakage without additional rule breaking.
BaseModel.cs
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
MessageBoxModel.cs
public class MessageBoxModel : BaseModel
{
private string msgboxcontent;
public string MsgBoxContent
{
get
{
return msgboxcontent;
}
set
{
this.msgboxcontent = value;
OnPropertyChanged("MsgBoxContent");
}
}
}
MessageBoxViewModel.cs // Child View-Model
public class MessageBoxViewModel
{
private MessageBoxModel MB;
public MessageBoxViewModel()
{
MB = new MessageBoxModel();
MB.msgboxcontent = "My Message Box Content";
}
public MessageBoxModel MessageBoxModel
{
get
{
return MB;
}
}
MainWindowViewModel.cs // Parent View-Model
public class MainWindowViewModel
{
private MessageBoxViewModel child_MsgBoxviewmodel;
public MainWindowViewModel()
{
child_MsgBoxviewmodel = new MessageBoxViewModel();
}
public MessageBoxViewModel MsgBoxViewModel
{
get
{
return child_MsgBoxviewmodel;
}
}
}
MainWindow.xaml.cs //Parent View
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
private void MessageBoxButton_Clicked(object sender, RoutedEventArgs e)
{
// Instantiate the dialog box
MessageBoxView dlgView = new MessageBoxView();
// Call parent view model to get child view model
MainWindowViewModel mvm = this.DataContext as MainWindowViewModel;
// Configure the dialog box
dlgView.DataContext = mvm.MsgBoxViewModel ;
// Open the dialog/message box
dlgView.ShowDialog();
}
}
MessageBoxView.xaml.cs //Child View
public partial class MessageBoxView : Window
{
public MessageBoxView()
{ //DialogBox
InitializeComponent();
}
}
The xmal files are not shown here because that one of the advantage of using MVVM. View styling is all up to the UI designer.
The message box will appear once someone click on the messageboxbutton.
Unit testing can then be done as usual on the model and viewmodel classes without worrying about popup windows during the test.
JP
Let me say first that I am also not a MVVM Purist but I can already spot one thing that from my point of view I would change in your code.
The button click event you have there should change to a binding in xaml to a ICommand of you MainWindowViewModel.
This will allow you to completely remove that code behind code, in the future you may also apply contracts (interfaces) with this approach to make your solution even more extensible :)
Cheers!

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();
}

Context Menu commands to dynamically create UI controls using MVVM

Hopefully this question isn't too general, but I have just started using WPF and I'm banging my head against this.
I am developing an application that uses dynamically created controls. However currently I can not figure out how to make the commands create and add more controls to the current window because the commands only work when created in the ViewModel which can not see the View. However I can't keep everything in the XAML because all controls except for a few initially empty stack panels are dynamic. I feel like I'm missing something easy here though.
So here I have the binding
<MenuItem Header="LabelMenuItem" Command="{Binding Path=SpawnLabel}"/>
And here I have the command
public ICommand SpawnLabel { get { return new DelegateCommand(OnSpawnLabel); } }
Delegate command works like a relay command as defined here.
public class DelegateCommand : ICommand
{
private readonly Action _command;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public DelegateCommand(Action command, Func<bool> canExecute = null)
{
if (command == null)
throw new ArgumentNullException();
_canExecute = canExecute;
_command = command;
}
public void Execute(object parameter)
{
_command();
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
}
This works in the view Model but I can't figure out how on earth to make it work in the View (or talk to the view without breaking MVVM principles) so that I can actually change the UI using the current controls created in c#.
Currently when I do it I get a BindingExpression path error which makes sense but I can not figure out how to bind it to look for the command in the view.
You indirectly communicate with the UI via events, if you change a property you fire PropertyChanged, the UI will update a binding, if you change a collection you fire CollectionChanged and a new control is added or an old one removed.
The important interfaces are INotifyPropertyChanged and INotifyCollectionChanged, just bind an ItemsControl (via ItemsSource) to an ObservableCollection<T> in your view-model and add to that collection in your command (you may need to use the ItemsControl.ItemTemplate to specify the view for your view-models, depending on what MVVM framework you use).

WFP DataGrid ItemsSource binding to ObservableCollection doesn't update beyond first setting?

I am binding a WPF application DataGrid to an ObservableCollection via the DataGrid's "ItemSource". Initially the DataGrid does come up with headings and values, however the upgrades made to the ObservableCollection are not reflected? (i.e. when I come back programmatically and increase the "Total" value) The ObservableCollection I am using is below.
Any ideas why & how to get the grid to dynamically update/bind correctly?
public class SummaryItem
{
public string ProcessName { get; set; }
public long Total { get; set; }
public long Average { get; set; }
public static SummaryItem ObservableCollectionSearch(ObservableCollection<SummaryItem> oc, string procName)
{
foreach (var summaryItem in oc)
{
if (summaryItem.ProcessName == procName) return summaryItem;
}
return null;
}
}
EDIT - Or perhaps an add-on question is whether in this case DataGrid isn't the control I should be using to visualize what is effectively an in-memory table? That is the observableCollection of SummaryItem's is effectively the in-memory table.
If I see it right you are using an ObservableCollection. If you add items to the ObservableCollection these changes should always been reflected by WPF, but if you edit properties on an item (i.e. changing the "Total" value of a SummaryItem) this is no change to the ObservableCollection but to the SummaryItem.
To achieve the desired behaviour your SummaryItems have to implement the INotifyPropertyChanged interface to "notify" WPF when propertys are changed:
// implement the interface
public event PropertyChangedEventHandler PropertyChanged;
// use this for every property
private long _Total;
public long Total {
get {
return _Total;
}
set {
_Total = value;
if(PropertyChanged != null) {
// notifies wpf about the property change
PropertyChanged(this, new PropertyChangedEventArgs("Total"));
}
}
}
you have just ran into the classic problem with ObservableCollection. Only item add and item remove events are fired for OC. That means, if an item changes, you do NOT get an "ItemChanged" event.
ObservableCollection only raise event when you add or remove items, if you need to raise event even if any item inside the collection changes use BindingList.

ReBind Controls using PRISM pattern in Silverlight

I have been trying to work with the Composite Application Library (Prism) and I have set up a pretty standard pattern that I have followed off Microsoft's tutorial. Basically, the View is injected into the Region. The View is dynamically built, adding controls and so forth all programmatically.
I have a command that gets fired and on postback I would like to rebind the controls on the current view, instead of completely re-rendering all the controls over again.
So I tried updating the model with the updated version hoping that would force a rebinding of controls. That doesn't work. Not sure what approach I should be taking, for I am new to Prism...
Any ideas?
Subscribe an event to handle postbacks
IEventAggregator aggregator = this.Container.Resolve<IEventAggregator>();
aggregator.GetEvent<DataInstanceLoadedEvent>().Subscribe(this.OnDataInstanceUpdated);
Implementation of the event
public void OnDataInstanceUpdated(DataInstance updatedInstance)
{
if(this.View.Model != null){
// We need to rebind here
IRegion region = this.LocateRegion(this.View); // gets the region....
this.View.Model.CurrentDataInstance = updatedInstance; // update the model instance
}
else{
// Render all controls over again since view.model is null ...
}
}
I have figured out how to rebind according to the suggested patterns from Microsoft.
Basically, all I had to do was inherit from INotifyPropertyChanged on my Model.
Then following this pattern, once my model updates it is then forced to rebind all the controls by firing an event that notifies the client that the property has in fact changed.
public class MyModel : INotifyPropertyChanged
{
private DataInstance currentDataInstance;
public event PropertyChangedEventHandler PropertyChanged;
public DataInstance CurrentDataInstance
{
get
{
return this.currentDataInstance;
}
set
{
if ( this.currentDataInstance == value )
return;
this.currentDataInstance = value;
this.OnPropertyChanged( new PropertyChangedEventArgs("CurrentDataInstance"));
}
}
protected virtual void OnPropertyChanged( PropertyChangedEventArgs e )
{
if ( this.PropertyChanged != null )
this.PropertyChanged( this, e );
}
}

Categories

Resources