Caliburn Micro : passing Object between ViewModel - c#

I'm developing a simple Crud Application (a windows 8.1 store application) using Caliburn Micro 2.0.0-alpha2
I'm in trouble with navigation between viewmodels, passing object.
I read many times the solution proposed by
Anders Gustafsson (How to pass parameter to navigated view model with WinRT Caliburn.Micro?)
and i tried to adapt it to my scope.
But the object is alwais null.
I need to pass a single object selected from a listView to my crudPage.
The crudPage is composed by an userControl that shown the FormView.
So i want to initialize this Form, with the values of the passed object.
I think that the problem is that the "Parameter" is initialized only after the ViewModel is created, but i don't know how to fix that problem.
There is my code, according with the idea of Anders Gustafsson
TransporterListViewModel (a list of Transporters from Database)
public class TransporterListViewModel : ViewModelBase
{
public string Title { get; set; }
public TransporterListViewModel(INavigationService navigationService)
: base(navigationService)
{
LoadData();
}
public async void LoadData() {
_transporters = await TransporterService.GetAll();
}
private BindableCollection<Transporter> _transporters;
public BindableCollection<Transporter> Transporters
{
get
{
return this._transporters;
}
set
{
this._transporters = value;
NotifyOfPropertyChange(() => this.Transporters);
}
}
private Transporter _selectedItem;
public Transporter SelectedItem
{
get
{
return _selectedItem;
}
set
{
_selectedItem = value;
NotifyOfPropertyChange(() => this.SelectedItem);
navigationService.Navigated += NavigationServiceOnNavigated;
navigationService.NavigateToViewModel<TransporterCrudPageViewModel>(_selectedItem;);
navigationService.Navigated -= NavigationServiceOnNavigated;
}
}
private static void NavigationServiceOnNavigated(object sender, NavigationEventArgs args)
{
FrameworkElement view;
TransporterCrudPageViewModel transporterCrudPageViewModel;
if ((view = args.Content as FrameworkElement) == null ||
(transporterCrudPageViewModel = view.DataContext as TransporterCrudPageViewModel) == null) return;
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
}
TransporterCrudViewModel (the page that cointains the UserControl to initialize)
public class TransporterCrudPageViewModel : ViewModelBase
{
public string Title { get; set; }
public Transporter Parameter { get; set; }
public TransporterFormViewModel TransporterFormVM { get; set; }
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
await SetUpForm(enumerable);
}
public async Task SetUpForm(Transporter t){
TransporterFormVM.trName = t.trName;
TransporterFormVM.trUrl = t.trUrl;
}
public TransporterCrudPageViewModel(INavigationService navigationService)
: base(navigationService)
{
Title = "TransporterCrud Page";
//this.navigationService = navigationService;
this.InitializeTransporterForm(Parameter);
}
TransporterFormViewModel (the userContol to initialize)
public class TransporterFormViewModel :ViewModelBase
{
public string Title { get; set; }
public Transporter Transporter { get; set; }
public TransporterFormViewModel(INavigationService navigationService,Transporter trans)
: base(navigationService)
{
Transporter = trans;
}
private string _trName;
public string trName
{
get
{
return _trName;
}
set
{
_trName = value;
NotifyOfPropertyChange(() => trName);
}
}
public string trCode { get; set; }
public string trUrl { get; set; }
public int trId { get; set; }

In the constructor TransporterCrudViewModel class you have:
this.InitializeTransporterForm(Parameter);
where Parameter is a property of type Transporter not initialized and you will call the method InitializeTransporterForm with a null parameter. Then you'll call SetUpForm method with a null value of the parameter Transporter t. I think you should initialize in some way this property.
Then, supposing you're continuing in your TransporterListViewModel class with this:
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
in the method InitializeTransporterForm, you don't set the passed parameter as value of the property Parameter with something like this:
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
this.Parameter = enumerable; //setting the Parameter property..
await SetUpForm(enumerable);
}
Beside these notes, you should put a breakpoint with your IDE in the line
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
Make sure that the property Parameter of the NavigationEventArgs object is not null.

Related

checking if datagrid has values

I am working on a C# project which includes WPF. I was wondering, If I could somehow check If my data grid contains certain element.
For example,
I have combo box whose itemsSource is some list of objects. Now, when an user selects an item from the combo box and presses the button
below in data grid (in same window) that item shows up.
I want to forbid the user to select same item more than once and for example put MessageBox with error message. How could I do that?
Code
This Window:
public partial class AvioWindowAddNEdit : Window
{
Avio avio;
public enum Stage { ADD, EDIT};
Stage stage;
public AvioWindowAddNEdit(Avio avio, Stage stage = Stage.ADD)
{
InitializeComponent();
this.avio= avio;
this.stage= stage;
textboxCode.DataContext = avio;
comboboxListofFlights.ItemsSource = Aplikacija.Instance.Flights;
comboboxListofFlights.DataContext = avio;
datagridListofFlights.ItemsSource = avio.ListofFlights;
datagridListofFlights.ColumnWidth = new DataGridLength(1, DataGridLengthUnitType.Auto);
if (stage== Stage.EDIT)
{
textboxCode.IsEnabled = false;
}
}
}
Button which adds selected item to data grid:
private void btnAddFlight_Click(object sender, RoutedEventArgs e)
{
avio.ListOfFlights.Add(comboboxListOfFlights.SelectedItem as Flight);
}
Singleton class for loading in all of my data:
class Aplication
{
public ObservableCollection<User> Users { get; set; }
public ObservableCollection<Airport> Airports { get; set; }
public ObservableCollection<Flight> Flights{ get; set; }
public ObservableCollection<Avio> Avios { get; set; }
public string LoggedInUser { get; set; }
private static Aplication instance = new Aplication();
public static Aplication Instance
{
get
{
return instance;
}
}
private Aplication()
{
Users= new ObservableCollection<User>();
Airports = new ObservableCollection<Airport>();
Flights = new ObservableCollection<Flight>();
Avios= new ObservableCollection<Avio>();
FillInData(); //method where I filled in all of these ObservableCollections
}
}
My class:
public class Avio : ObservableObject, ICloneable
{
//observableobject is an object where I implemented INotifyPropertyChanged
private string code;
public string Code
{
get { return code; }
set { code= value; OnPropertyChanged("Code"); }
}
private ObservableCollection<Flight> listOfFlights;
public ObservableCollection<Flight> ListOfFlights
{
get { return listOfFlights; }
set { listOfFlights= value; OnPropertyChanged("ListOfFlights"); }
}
private bool active;
public bool Active
{
get { return active; }
set { active= value; OnPropertyChanged("Active"); }
}
public Avio()
{
active= true;
ListOfFlights = new ObservableCollection<Flight>();
}
public Avio(string code)
{
active= true;
ListOfFlights = new ObservableCollection<Flight>();
Code= code;
}
}
You could use an ObservableCollection as an ItemsSource for your DataGrid. In that way you'll always have easy access to the data via code.
Check out this tutorial as a starting point (this uses ListBox instead of DataGrid, but it's easily adaptable to DataGrid).

How to observe multiple properties with Delegatecommand.ObservesProperty

I would like to observe multiple properties in my viewmodel to check the CanExecute Method.
Question:
How to register more properties?
Example:
class MyViewModel
{
public int myproperty1 { get; set; }
public int myproperty2 { get; set; }
public DelegateCommand MyCommand { get; set; }
public MyViewModel()
{
MyCommand = new DelegateCommand(MyCommandMethod,CanExecuteMyCommandMethod);
MyCommand.ObservesProperty((() => myproperty1));
// line below doesnt work Exeception "Value is already observed". How to register more properties to observe?
MyCommand.ObservesProperty((() => myproperty2));
}
private bool CanExecuteMyCommandMethod()
{
throw new NotImplementedException();
}
private void MyCommandMethod()
{
throw new NotImplementedException();
}
}
Update:
PropertChanged event is done by Propertchanged.Fody INPC.
ObservesProperty is to RaiseCanExecuteChanged.
The second property observe registration raise a exception "Value is already observed).
Additional Question:
ObservesProperty((() => myproperty;
Allows multiple or single property?
If somebody else confirm multiple registration shall be possible?
Calling the ObservesProperty method for another property like you are doing should work. But you need to raise the PropertyChanged event in the setters of the source properties for the CanExecuteChanged event to be raised.
Please refer to the following sample code.
public class MyViewModel : BindableBase
{
private int _myproperty1;
public int myproperty1
{
get { return _myproperty1; }
set { _myproperty1 = value; RaisePropertyChanged(); }
}
private int _myproperty2;
public int myproperty2
{
get { return _myproperty2; }
set { _myproperty2 = value; RaisePropertyChanged(); }
}
public DelegateCommand MyCommand { get; set; }
public MyViewModel()
{
MyCommand = new DelegateCommand(MyCommandMethod, CanExecuteMyCommandMethod);
MyCommand.ObservesProperty((() => myproperty1));
MyCommand.ObservesProperty((() => myproperty2));
}
private bool CanExecuteMyCommandMethod()
{
return true;
}
private void MyCommandMethod()
{
}
}

Raise event from Inner class to outer class in c#

I'm using MVVM pattern in my project.
My class design is like this:
Class Model : AbstractModel
{
InnerClass Iclass = new InnerClass();
Public String ModelProp1
{
get
{
return Iclass.prop1;
}
set
{
Iclass.prop1 = value;
}
}
public override void SetLabel(UInt16 value, int Index)
{
byte[] arr = BitConverter.GetBytes(value);
this.Iclass.IclassConfig[Index].Label = arr[0];
}
public override string DateFormat
{
get { return Iclass.intlDate.ToString(); }
set { Iclass.intlDate = Convert.ToByte(value); }
}
}
Class InnerClass
{
public byte intlDate
{
get { return this.intl_date; }
set { this.intl_date = value;
RaiseModelPropertiesChangedEvent(new ValueChangedEventArgs { Parameter_dateformat = this.intlDate });
}
private JClassa []channel_config = new JClass[2];
public JClass[] IclassConfig
{
get { return this.channel_config; }
set { this.channel_config = value; }
}
}
Public JClass
{
private byte channel_label;
public byte Label
{
get { return this.channel_label; }
set { this.channel_label = value;}
}
I'm getting data from other application. updated data is coming in InnerClass property from there I want to push this updated data to Model class.
Problem is coming for JClass property how can I fire event such that It will push updated data to model class.
For this I have created Event in InnerClass like this:
public event EventHandler<ValueChangedEventArgs> ModelPropertiesChanged;
public void RaiseModelPropertiesChangedEvent(ValueChangedEventArgs e)
{
if (ModelPropertiesChanged != null)
ModelPropertiesChanged(this, e);
}
public class ValueChangedEventArgs : EventArgs
{
public int Parameter_dateformat { get; set; }
public int Parameter_channelLabel { get; set; }
}
Tell me how Can I achieve this. Becuase I have 4 property in Jclass and 6 Property is InnerClass.
I would add event triggers in the setter of your inner class properties. Then in the Constructor of your parent class, move the IClass = new InnerClass() into your constructor and attach your event listeners.
Since you're MVVM you could leverage INotifyPropertyChanged, but heat will get messy in the long run.
Better to have a 'PropertyName'Changed event for each property you want to notify to the parent class.

Implement many INotifyPropertyChanged

please tell me best way to implement many duplicate INotifyPropertyChanged.
I have a MainClass that has 10 children, every child has six field and every field must fired property change when own value changed.
this my code but not work:
public class BaseModel
{
public string S1 { get; set; }
public string S2 { get; set; }
public string S3 { get; set; }
public string S4 { get; set; }
public string S5 { get; set; }
public string S6 { get; set; }
}
and I use a class named ViewModelBase to implement INotifyPropertyChanged.
in second step use a class to implement duplicate INotifyPropertyChanged:
public class ImplementBaseModel : ViewModelBase
{
private readonly BaseModel _baseModel;
public ImplementBaseModel()
{
_baseModel = new BaseModel();
}
public string S1
{
get { return _baseModel.S1; }
set
{
if (_baseModel.S1 == value)
return;
_baseModel.S1 = value;
base.OnPropertyChanged("S1");
}
}
public string S2
{
get { return _baseModel.S2; }
set
{
if (_baseModel.S2 == value)
return;
_baseModel.S1 = value;
base.OnPropertyChanged("S2");
}
}
// other code...
}
then a model has 10 of this class:
public class MidClass
{
public ImplementBaseModel ImplementBaseModel1 { get; set; }
public ImplementBaseModel ImplementBaseModel2 { get; set; }
// other field
public ImplementBaseModel ImplementBaseModel10 { get; set; }
public MidClass()
{
ImplementBaseModel1 = new ImplementBaseModel();
ImplementBaseModel2 = new ImplementBaseModel();
// ....
ImplementBaseModel10 = new ImplementBaseModel();
}
}
OK finish code! now please tell me why some property not fired when value change? is a best way to implement this code?
In your setters, you never actually set the value. Use:
public string S1
{
get { return _baseModel.S1; }
set
{
if (_baseModel.S1 == value)
return;
baseModel.S1 = value;
OnPropertyChanged("S1");
}
}
Note that I removed the base from OnPropertyChanged. It isn't normal to invoke the PropertyChanged event in this way.
All NotifyPropertyChanged does is cause every binding to perform a "get" on their bound property. If the backing field is never updated, they will just get the same data.
as a shortcut, you could also create a local method like
bool UpdateAndRaiseIfNecessary( ref string baseValue, string newValue, [CallerMemberName] string propertyName = null)
{
if (baseValue != newValue)
{
baseValue = newValue;
OnPropertyChanged( propertyName );
return true;
}
return false;
}
and then all of the setters would be like this:
set
{
this.UpdateAndRaiseIfNecessary( ref _baseModel.S1, value );
}

Is there a name for this creation pattern?

What should I be calling the "BFactory" below. Its not really a Factory since there is no selection of a concrete class happening, and its not necessarily creating an object each time. Its kind of a Pool but the users do not return the Bs they get to the pool after they are done with them. It could be called a Cache but performance is not the primary intention. The intention is that everyone who is using the same BFactory will get the same B when they pass the same A which starts to sound kind of like a singleton-ish.
public class A
{
public int MyProperty { get; set; }
}
public class B
{
public B(A wrapped)
{
Wrapped = wrapped;
}
public A Wrapped { get; set; }
}
public class BFactory
{
private Dictionary<A,B> _created = new Dictionary<A,B>();
public B GetB(A a)
{
if (_created.ContainsKey(a) == false)
{
_created[a] = new B(a);
}
return _created[a];
}
}
here is a slightly more real example:
The value from MyModel is shown in several locations in the app by binding a TextBlock to the ValueString property of MyViewModel. The user can select to present the value as a percent or a decimal and it should be updated in all locations if it is updated in one.
public class MyModel
{
public int Value { get; set; }
}
public class MyViewModel
{
private readonly MyModel _model;
public MyViewModel(MyModel model)
{
_model = model;
}
public string ValueString
{
get { return string.Format(FormatString, _model.Value); }
}
public string FormatString { get; set; }
}
public class MyViewModelFactory
{
private readonly Dictionary<MyModel, MyViewModel> _created = new Dictionary<MyModel, MyViewModel>();
public MyViewModel GetViewModel(MyModel model)
{
if (_created.ContainsKey(model) == false)
{
_created[model] = new MyViewModel(model);
}
return _created[model];
}
}

Categories

Resources