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 );
}
}
Related
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.
I have a question, I am building small login system, basically its ready and working, but still having some problems with UI, si if I take such button click action
private void LoadButton_Click(object sender, RoutedEventArgs e)
{
Nullable<bool> creditencialFile = _controls.CredencialsFileDialog.ShowDialog();
if (creditencialFile == true)
{
ContextStatic.Filename = _controls.CredencialsFileDialog.FileName;
FileInfo creditencialsFileInfo = new FileInfo(ContextStatic.Filename);
ContextStatic.RootFolder = creditencialsFileInfo.DirectoryName;
model.LeapCreditencials = CredentialHelper.LoadCredentials(ContextStatic.Filename);
}
}
It loads credentials from file, and they are saved in object attribute:
model.LeapCreditencials = CredentialHelper.LoadCredentials(ContextStatic.Filename);
Now i want to refresh or reload UI so I all information windows would be set up to with new info. Question is should I need to reload per one control, or there is a smart way to reload Ui with new object values?
Yes, you should implement INotifyPropertyChanged in your model
msdn description to implement INotify Interface
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
When value of model is changed it will reflect in the UI.
Xaml
<TextBox Text="{Binding Mymodel.CustomerName,
UpdateSourceTrigger=PropertyChanged}" />
Model
public class DemoCustomer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
Yeah, there's a smart way. It's called MVVM (a.k.a. Model View View Model). It is not so hard to understand. You just bind your view to values in ViewModel, and when a value is changed UI is automatically updated.
I'm using mvvmcross and xamarin to bind an ObservableCollection to a UITableView. The collection is updated in place using the Add, Remove and Move methods. These calls correctly trigger INotifyCollectionChanged events and the TableView is updated as expected the first time the view containing the table is shown. If the user navigates away from the original view as part of the normal application flow but later returns the correct data is loaded into the table but calls to add, move and remove no longer update the table.
The INotifyCollectionChanged events are still being fired when the collection is updated
If I manually subscribe to these events in my subclass of MvxStandardTableViewSource and try and call ReloadData on the UITableView still does not update
My presenter is creating a new instance of the viewmodel and view each time the page is visited.
I'm also using Xamarin-Sidebar (https://components.xamarin.com/view/sidebarnavigation) for navigation in my application with a custom presenter to load the views but as far as I can tell the view is initialised via exactly the same code path whether it's the first or subsequent visit.
My presenters Show() method looks like this:
public override void Show(MvxViewModelRequest request)
{
if (request.PresentationValues != null)
{
if(NavigationFactory.CheckNavigationMode(request.PresentationValues, NavigationFactory.ClearStack))
{
MasterNavigationController.ViewControllers = new UIViewController[0];
base.Show(request);
}
else if(NavigationFactory.CheckNavigationMode(request.PresentationValues, NavigationFactory.LoadView))
{
var root = MasterNavigationController.TopViewController as RootViewController;
var view = this.CreateViewControllerFor(request) as UIViewController;
root.SidebarController.ChangeContentView(view);
}
}
else
{
base.Show(request);
}
}
The binding in my ViewController looks like this:
public override void ViewDidLoad()
{
base.ViewDidLoad();
View.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
var source = new TracksTableSource(TableView, "TitleText Title; ImageUrl ImageUrl", ViewModel);
TableView.Source = source;
var set = this.CreateBindingSet<TracksViewController, TracksViewModel>();
set.Bind(source).To(vm => vm.PlaylistTable);
set.Apply();
}
And my viewmodel is as below where PlaylistTable is a subclass of ObservableCollection with the Update method using add, move and remove to keep the collection up to date.
public class TracksViewModel : MvxViewModel
{
private readonly IPlaylistService _playlistService;
private readonly IMessengerService _messengerService;
private readonly MvxSubscriptionToken _playlistToken;
public PlaylistTable PlaylistTable { get; set; }
public TracksViewModel(IPlaylistService playlistService, IMessengerService messengerService)
{
_playlistService = playlistService;
_messengerService = messengerService;
if (!messengerService.IsSubscribed<PlaylistUpdateMessage>(GetType().Name))
_playlistToken = _messengerService.Subscribe<PlaylistUpdateMessage>(OnDirtyPlaylist, GetType().Name);
}
public void Init(NavigationParameters parameters)
{
PlaylistTable = new PlaylistTable(parameters.PlaylistId);
UpdatePlaylist(parameters.PlaylistId);
}
public async void UpdatePlaylist(Guid playlistId)
{
var response = await _playlistService.Get(playlistId);
PlaylistTable.Update(new Playlist(response));
}
private void OnDirtyPlaylist(PlaylistUpdateMessage message)
{
UpdatePlaylist(message.PlaylistId);
}
}
This setup works perfectly the first time the view is initialised and updates the table correctly, it's only the second and subsequent times the view is initialised that the table fails to update. Can anyone explain why the binding fails when it appears the view is created using the same techniques in both instances?
I can post additional code if required but I believe the issue will be how I'm using the presenter since the code I've not posted from PlaylistTable functions correctly in unit tests and on first viewing.
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();
}
Hey guys I have problem with binding and refreshing binding.
I am using Entity Framework with WindowsForms...
I am retrieving orders from Shipping Queue and binding them to the grid.
if I open another form and and move Order X to different Queue, my grid does not reflect that...
So for example, the Main form has a two grids and a button
Grid 1 = Orders in Shipping Queue
Grid 2 = Orders in New Order Queue
Button 1 = Manage Orders
If I click my "Manage Orders" button and open up Order X that is in the Shipping Queue and move it to the New Orders Queue, I want the change to be reflected in the grids.
I tried different stuff and the cheapest and best solution I came up with was to call update on the grids every few minutes but I feel that there must be a better way...
Any thoughts?
Ensure that the values you're binding to are calling OnPropertyChanged() properly.
public class Class1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int myValue;
public int MyValue
{
get { return myValue; }
set
{
if (myValue != value)
{
myValue = value;
OnPropertyChanged("MyValue");
}
}
}
protected virtual void OnPropertyChanged(string property)
{
var notify = PropertyChanged;
if (notify != null)
notify(this, new PropertyChangedEventArgs(property));
}
}