Property Change not being updated in the View dynamically - c#

My binding is not updating the View in real-time when an OnPropertyChanged is called. I am able to set breakpoints to see the value being changed in the View Model. The View eventually updates when I make another selection, but not in real-time. I believe I know what the problem is, but I am struggling with fixing the problem. I think the problem is that I am calling the CreateChartNode method that is creating a new instance.
In my View Model, it looks like this:
public class ChartObjectVM : INotifyPropertyChanged
public ChartNode
{
get
{
return CreateChartNode();
}
}
private string fullText;
public string FullText
{
get
{
return this.fullText;
}
set
{
this.fullText = value;
OnPropertyChanged("FullText");
}
public ChartNode CreateChartNode()
{
ChartNode newChartNode = new ChartNode();
And in my ViewModel I am doing:
public void CreateNode()
{
ChartObjectVM cObjectVM = new ChartObjectVM();
ChartNode = cObjectVM.CreateNode();
}
My binding in the View looks like:
{Binding Path = SelectedChartObject.UserObject.FullText, UpdatedSourceTrigger=PropertyChange, Mode=TwoWay}
Where SelectedChartObject is the currently selected ChartObjectVM.
Like I have said, I am pretty sure the problem is the CreateNode() method being called. I think there are two instances under the hood but I can't figure out why.

Related

Extending dynamic dispatch to call functions in the view model?

I'm using MVVM in a Xamarin application, I have an interface to navigate between pages:
public interface INavigate
{
INavigate Next();
INavigate Previous();
string ViewTitle { get; }
}
In the implementing views:
public partial class V2Upload : ContentView, INavigate
{
public string ViewTitle => "Upload photos";
public INavigate Next()
=> new V3AdDetail();
public INavigate Previous()
=> new V1Agreement();
}
and in the view model
I have a property of type INavigate:
public INavigate CurrentAddItemStep
{
get { return _currentAddItemStep; }
set { Set(ref _currentAddItemStep, value); }
}
and the Content property of the parent view is bound to this property:
when next button is clicked I execute this code:
CurrentAddItemStep = CurrentAddItemStep.Next();
ViewTitle = CurrentAddItemStep.ViewTitle;
now a validation method is required before navigating to the next page for all the Content views..
I want to keep the MVVM pattern as clean as possible by not writing business code in the view, for example in the V2Upload view the File1 and File2 properties of the view model shouldn't be null:
private bool ValidateFiles(){
return (File1 ?? File2) != null;
}
but since the navigating is done dynamically in run-time, I can't know which view is the current view.
I'm thinking to use reflection , to know what is the name of the view (but this will break the whole design)
Another option is to provide a function parameter to the Next method, but also how to provide it in the design time from the view model?
This is what I'm doing now:
public INavigate Next()
{
if (((ViewModel.AddItemViewModel)BindingContext).ValidateFiles())
return new V3AdDetail();
else
return this;
}
but again, I'm accessing the view model from the view (and had to change the ValidateFiles method from private to public), which I want to avoid

Displaying and updating table thats boxed in a class in my view

Im having issues getting data to appear in a datagrid in my view.
public class ComputerViewModel: ObservableObject
{
private ObservableCollection<ComputerModel> _ComputerInformation;
public ObservableCollection<ComputerModel> ComputerInformation {
get { return _ComputerInformation; }
set {
_ComputerInformation = value;
NotifyPropertyChanged("ComputerInformation");
}
}
public ComputerViewModel()
{
ComputerInformation = new ObservableCollection<ComputerModel>();
ComputerModel computer = new ComputerModel();
computer.start("DA2968");
ComputerInformation.Add(computer);
}
}
public class ComputerModel: ObservableObject
{
private DataTable _ProcessHistory;
public DataTable ProcessHistory {
get { return _ProcessHistory; }
set { _ProcessHistory = value; NotifyPropertyChanged("ProcessHistory"); }
}
}
View:
<DataGrid DataContext="{Binding Source=ComputerInformation}" ItemsSource="{Binding ProcessHistory}"/>
im not sure its even possible.
Basically i have code that monitors process creation and deletion on multiple computers.
I intitate a new model for each computer and would like to store them in the ViewModel.
which is working fine. im just having difficulty showing the information on the screen. can anyone please help.
ObservableObject is where my INotifyPropertyChanged and DelegateCommand is located.
ComputerModel is where the code for monitoring the creation and deletion of process is located. this is all working fine and storing in the DataTable fine. the data is there i just cannot display it.
Thanks

Caliburn.Micro WindowManager Cannot find view

I have done some searching and I can't find anyone with my specific problem.
I have a Caliburn.Micro project and I successfully have a main view with sub-views inside it which is not a problem. My View Models are in a different assembly to my views.
This meant I had to override SelectAssemblies to include my view models project:
protected override IEnumerable<Assembly> SelectAssemblies()
{
var assemblies = base.SelectAssemblies().ToList();
assemblies.Add(typeof(OrderViewModel).Assembly);
return assemblies;
}
Now, this is where my confusion starts. I successfully have a OrderView showing the OrderViewModel. Inside that there is a KeyboardViewModel with a KeyboardView. This all works fine so caliburn is finding the right assemblies etc.
However when I come to use the window manager to display a new view/viewmodel which is passed into the order view. I am getting a screen with the text "Cannot find view model for XX.ViewModels.Model."
This is my OrderViewModel
[Export(typeof(OrderViewModel))]
public class OrderViewModel : Screen
{
private readonly IWindowManager windowManager;
private ISession session;
[ImportingConstructor]
public OrderViewModel(IWindowManager windowManager, KeyboardViewModel keyboardViewModel)
{
TillDatabase.CreateInstance(ApplicationConfiguration.Instance.DatabaseConnectionString);
this.windowManager = windowManager;
this.Keyboard = keyboardViewModel;
this.Keyboard.Order = this;
this.Keyboard.Home();
}
public void ChangePriceBand()
{
windowManager.ShowWindow(new PriceBandSelectionViewModel(this));
}
}
The thing is, I even tried this in ChangePriceBand
windowManager.ShowWindow(new OrderViewModel(this.windowManager, new KeyboardViewModel()));
And this gets the same error. Even though a view has already been associated with the OrderViewModel previously!!
This is the PriceBandSelectionViewModel just in case.
[Export(typeof(PriceBandSelectionViewModel))]
public class PriceBandSelectionViewModel : Screen
{
private OrderViewModel order;
[ImportingConstructor]
public PriceBandSelectionViewModel(OrderViewModel order)
{
this.order = order;
}
public ObservableCollection<PriceBandButtonViewModel> Buttons
{
get
{
var list = new ObservableCollection<PriceBandButtonViewModel>();
var priceBands = this.order.Session.QueryOver<Application_Model_PriceBand>().List();
foreach (var priceBand in priceBands)
{
PriceBandButtonViewModel button = new PriceBandButtonViewModel(priceBand, this);
list.Add(button);
}
return list;
}
}
public void ProcessButtonClick(Application_Model_PriceBand button)
{
this.order.ChangeCurrentPriceBand(button);
base.TryClose();
}
}
I'm just really confused to how Caliburn is setting up my main view, but the window manager isn't even though its the same ViewModel?
have you tried to remove OrderViewModel or put a breakpoint there, cant find view error might happen if it encountered error when initialising the exported class
public PriceBandSelectionViewModel()
{
// this.order = order;
}
or add
assemblies.Add(typeof(PriceBandSelectionViewModel).Assembly);
This may be the same problem as I am experiencing as described here: Caliburn.Micro HelloWindowManager Sample - View location not working
To see if it is the same problem, try changing the call from
windowManager.ShowWindow(new PriceBandSelectionViewModel(this));
to
windowManager.ShowDialog(new PriceBandSelectionViewModel(this));.
In my case, ShowDialog was able to locate the view no problem, but ShowWindow and ShowPopup were not.

Pass a parameter to a ViewModel and show its data

I have created an application which uses WPF and MVVM following this article from CodeProject.
I have a view, TVSeriesView, which has a TVSeriesViewModel. These two are connected using a DataTemplate which is done following the article.
<DataTemplate DataType="{x:Type Implementation:TVSeriesViewModel}">
<TVSeriesLibrary:TVSeriesView />
</DataTemplate>
The idea is to pass my model, the TVSeries, to this ViewModel as I have a property named TVSeries in the ViewModel. When this property is set, I will populate other properties such as Title, Cover and so on. These properties are meant to be binded to controls in the view.
public class TVSeriesViewModel : ViewModelBase, ITVSeriesViewModel
{
private TVSeries _tvSeries;
private string _title;
private ImageSource _cover;
public TVSeries TVSeries
{
get
{
return this._tvSeries;
}
set
{
this._tvSeries = value;
}
}
public string Title
{
get
{
return this._title;
}
set
{
this._title = value;
OnPropertyChanged("Title");
}
}
public ImageSource Cover
{
get
{
return this._cover;
}
set
{
this._cover = value;
OnPropertyChanged("Cover");
}
}
}
First and foremost, does this sound like the right way to do it?
Next, does anyone know how to pass a parameter (a TVSeries object) to the ViewModel when the TVSeriesView is shown?
And lastly, does anyone know how I can directly access resources in the view? For example if I don't want to use data binding but instead want to set the image directly like this:
myImage.ImageSource = myImageSource
The View and ViewModel together are one of the possible representations of the Model.
You can pass a repository handle which would be eventually responsible for data access or
Concrete/abstract object of Model through Dependency Injection via Constructor or
Dependency Injection, via property/method or
In more crude way you can write a DB access code in your VM (obviously it's not suggested.)
I would prefer as the order given here. Your code is doing the third option.

Passing DataContext between windows in MVVM

On the main window onClick I have
AddNoticeAboutWrongCity addNoticeAboutWrongCity = new AddNoticeAboutWrongCity();
addNoticeAboutWrongCity.DataContext = ((VerificationViewModule)this.DataContext).WrongCityNotice;
addNoticeAboutWrongCity.ShowDialog();
At popup window there a lot of textboxes and two buttons
Delete object:
this.DataContext = null;
And second option "Save edited notice" which is not usable , because every change of user affection datacontext on main window,and this is demand from design department :)
I don't know why first option(it's "implementation" doesn't work.
Second explanation:
On the ParentWindow I have list of Notices and I can click EditSelectedNotice.
On the EditNoticeWindow I can edit Notice or delete Notice.
Editinig works(After closing EditNoticeWindow I see changed notice on the ParentWindow), but deleting doesn't (Notice is still in collection - on control and in this.DataContext)
My ViewModel:
class VerificationViewModule
{
public ObservableCollection<ReporterNotice> ReporterNotices { get; set; }
public ReporterNotice OtherNotice
{
get
{
return ReporterNotices.Where(n => n.Type == ReporterNoticeType.Other).FirstOrDefault();
}
}
public ReporterNotice DuplicateNotice
{
get
{
return ReporterNotices.Where(n => n.Type == ReporterNoticeType.Duplicate).FirstOrDefault();
}
}
public ReporterNotice WrongCityNotice
{
get
{
return ReporterNotices.Where(n => n.Type == ReporterNoticeType.WrongCity).FirstOrDefault();
}
set { if(value==null)
{
ReporterNotices.Remove(ReporterNotices.Where(n => n.Type == ReporterNoticeType.WrongCity).First());
}
else
{
if (ReporterNotices.Where(n => n.Type == ReporterNoticeType.WrongCity).FirstOrDefault()==null)//there is always only max one instance of this type of notice
{
ReporterNotices.Add(value);
}
else
{
var c = ReporterNotices.Where(n => n.Type == ReporterNoticeType.WrongCity).First();
c = value;
}
}}
}
public VerificationViewModule()
{
ObservableCollection<ReporterNotice> loadedReporterNotices = new ObservableCollection<ReporterNotice>();
loadedReporterNotices.Add(new ReporterNotice() { Content = "Dublic", Type = ReporterNoticeType.WrongCity });
loadedReporterNotices.Add(new ReporterNotice() { Content = "Hilton", Type = ReporterNoticeType.Duplicate });
loadedReporterNotices.Add(new ReporterNotice() { Content = "Another notice", Type = ReporterNoticeType.Other });
ReporterNotices = loadedReporterNotices;
}
}
You can try the following. Implement the mediator to display windows and make sure that you use view models for the DataContext for both the main and edit windows. It is important to tell the main view model that the object is being deleted. This is done via a callback and routing that through a command on the EditNoticeViewModel
//This viewmodel is on the main windows datacontext
public class ParentViewModel
{
private readonly IWindowMediator _mediator;
public ParentViewModel(IWindowMediator mediator)
{
_mediator = mediator;
}
public ObservableCollection<Notice> Notices { get; private set; } //bound to list in xaml
public void OpenNotice(Notice notice)
{
//open the window using the Mediator pattern rather than a new window directly
_mediator.Open(new EditNoticeViewModel(notice, DeleteNotice));
}
private void DeleteNotice(Notice notice)
{
//This will remove it from the main window list
Notices.Remove(notice);
}
}
//view model for EditNoticeWindow
public class EditNoticeViewModel
{
public EditNoticeViewModel(Action<Notice> deleteCallback, Notice notice)
{
Model = notice;
DeleteCommand = new DelegateCommand((a) => deleteCallback(Model));
}
//Bind in xaml to the Command of a button
DelegateCommand DeleteCommand { get; private set; }
//bound to the controls in the xaml.
public Notice Model { get; private set; }
}
//This is a basic interface, you can elaborate as needed
//but it handles the opening of windows. Attach the view model
//to the data context of the window.
public interface IWindowMediator
{
void Open<T>(T viewModel);
}
Depending on implementation you might want to close the view when the delete button gets pushed. You can do this by implementing something like the as described here with respect to WorkspaceViewModel
Why don't you wrap the WrongCityNotice in a viewModel implementing IReporterNotice and having a reference to the parent viewmodel and a Delete method:
public void Delete() { _parentvm.Delete(_wrongCityNotice); }
You can use this wrapper as DataContext.
You're trying to destroy the DataContext. C# doesn't work that way. Setting an object reference to null doesn't delete the object, it only removes the reference to it. (When nothing references an object anymore it gets garbage collected, but you can't destroy an object directly).
DataContext = null only means that locally your DataContext doesn't point to any object any more. The main view model still has a reference however so nothing changes there. You'll have to ask the main view model to remove the notification from it's collection (probably through a callback method (Action) is best so you don't have to know about the parent view model).

Categories