Please can you advise if this is possible or what approach is best?
I will add my code afterwards but I am worried that it will add limited value to what I am trying to ask/explain.
The WPF examples that I have seen implement
A (poco) model e.g Customer.
Then they implement a view model (MVVM pattern). (The View Model needs to implement ObservableCollection and or implement INotifyPropertyChanged interface so that any changes in the model are reflected in the UI once the model changes.)
In the xaml code behind, in the page constructor, the ViewModel is passed to the DataContext.
The xaml can then bind to this View Model Data with a Mode of update e.g. TwoWay.
Here is what I need to understand.
I have implemented my own data model / class, which has async tasks constantly updating the status of different fields in this class.
My model resides in a separate class library that I would like to inject/supply it to a view model. However, since my object/class is 'self-updating', I can't simply copy across values into my view model - since they will change over time. I want my view model to be aware of changes that underlying values and show these changes in the UI as the async tasks update the model.
How do I go about this? So in my example, my Customer object will be self-updating, some background task would add/remove customers in a class library outside of my ViewModel.
I hope that I have managed to ask my question in a way that is clear to understand.
The XAML binding to the Customer View Model
<ListView Grid.Row="1" ItemsSource="{x:Bind ViewModel.Customers,Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedCustomer,Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Customer">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind FirstName,Mode=OneWay}" FontWeight="Bold"/>
<TextBlock Text="{x:Bind LastName,Mode=OneWay}"
Margin="5 0 0 0"/>
<TextBlock Text="(Developer)" Margin="5 0 0 0" Opacity="0.5"
Visibility="{x:Bind IsDeveloper,Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Reference for this sample code
public class MainViewModel : Observable
{
private ICustomerDataProvider _customerDataProvider;
private Customer _selectedCustomer;
public MainViewModel(ICustomerDataProvider customerDataProvider)
{
_customerDataProvider = customerDataProvider;
Customers = new ObservableCollection<Customer>();
}
public Customer SelectedCustomer
{
get { return _selectedCustomer; }
set
{
if (_selectedCustomer != value)
{
_selectedCustomer = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsCustomerSelected));
}
}
}
public bool IsCustomerSelected => SelectedCustomer != null;
public ObservableCollection<Customer> Customers { get; }
public async Task LoadAsync()
{
Customers.Clear();
var customers = await _customerDataProvider.LoadCustomersAsync();
foreach (var customer in customers)
{
Customers.Add(customer);
}
}
public async Task SaveAsync()
{
await _customerDataProvider.SaveCustomersAsync(Customers);
}
public void AddCustomer()
{
var customer = new Customer { FirstName = "New" };
Customers.Add(customer);
SelectedCustomer = customer;
}
public void DeleteCustomer()
{
var customer = SelectedCustomer;
if (customer != null)
{
Customers.Remove(customer);
SelectedCustomer = null;
}
}
}
The INotifyPropertyChanged is implemented here:
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Thanks #Bandook for your comments. Sometimes the coding is the easy part, but the thought behind and the process of fitting the parts together in a simple and coherent way can be more difficult.
The solution I implemented was as follows:
As per the customer example, I had had to implement methods that updated my class properties that implemented the INotifyPropertyChanged interface. (This was almost identical to the Customer code.) The result of this is that any changes to the underlying model are reflected in the UI.
The kicker was this. I implemented a Publish Subscribe Design Pattern in my class library - that continually refreshed the data. My 'Customer' class had to then had to subscribe to change events published by the class library.
There was one problem, however, the thread for the Class Library code and that of the application thread was not the same, resulting in a clash.
The article here allowed me to solve this issue.
In summary, the solution was to implement a Publish Subscribe Design Pattern. My class library published updates and my View Model class (similar to the customer class) subscribed to the events it published.
Related
I am using MVVM with ReactiveUI. I have a property in the model that I want to display and be able to edit in the UI. Is there any simple way to do this using ReativeUI? The following properties should be fulfilled:
The model property implements INotifyPropertyChanged
The model property can be changed from the view model or from the model
Updates from within the model can be made on any thread
Updates from the view model should use a throttle so that not every keystroke becomes a model update
The application can be run with a UI or command line only, and the code should also be runnable in unit tests and integration tests
When using an UI, the PropertyChanged event of the ViewModel needs to be raised on the UI thread
The throttle can't be blocking in either run mode
The code should be robust, i.e. not have the risk of causing deadlocks or reverting back to old values.
I somehow imagined this would be a standard case of how to wire a view model to a model but haven't managed to get this to work, and can't really figure out any way to make it work without quite a lot of code for a seemingly simple task.
Sample code of a non-working implementation:
public interface IModel : INotifyPropertyChanged
{
string MyProperty { get; set; }
}
public class SomeViewModel : ReactiveObject
{
private readonly IModel model;
public SomeViewModel(IModel model)
{
MyProperty = String.Empty;
this.model = model;
var inputThrottleTime = TimeSpan.FromMilliseconds(500);
var scheduler = RxApp.MainThreadScheduler is AvaloniaScheduler
? RxApp.MainThreadScheduler
: RxApp.TaskpoolScheduler;
// This doesn't work. If updates are made in the model inputThrottleTime apart, the old value might be reassigned to the model.
// And also, WhenAnyValue shouldn't be used to listen on properties that might be updated on background threads according to ReactiveUI devs.
this.WhenAnyValue(x => x.model.MyProperty).ObserveOn(scheduler).Subscribe(p => MyProperty = p);
this.WhenAnyValue(x => x.MyProperty).Skip(1).Throttle(inputThrottleTime, scheduler)
.Subscribe(p => model.MyProperty = p);
}
[Reactive] public string MyProperty { get; set; }
}
You can use data binding. Chose your control component in view(xaml). After that you give a datacontext for this view. There are different ways for giving data context and item source for controls. You can do this in xaml file like this:
<Window.DataContext>
<vm:ViewModels.MainWindowViewModel />
</Window.DataContext>
<TextBlock Text="{Binding Name}"/>
After that you can access variables. This variables must have get set properties and you can use ReactiveUI in here.
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
I used this in Avalonia. For more information you can look this: https://docs.avaloniaui.net/docs/data-binding
I'm wondering what is the best way to pass a file between pages in a UWP app?
I have a UWP app with two pages. In the first page, I have the user open a file with filepicker and load that file into a media player.
I want to pass that same file onto the second page when the user navigates there. I am passing the file over currently as a string which I then am attempting load as a storagefile using GetFileFromPathAsync.
This currently works as I'm able to load the file on the second page but it requires that the user provide broad file system access.
Code on Page 1 (FileLoaded is file path string):
private async void TranscodeMedia_Click(object sender, RoutedEventArgs e)
{
AppWindow appWindow = await AppWindow.TryCreateAsync();
Frame appWindowContentFrame = new Frame();
appWindowContentFrame.Navigate(typeof(TranscodeMedia), FileLoaded);
Code on Page 2:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
var fileTransfer = e.Parameter.ToString();
FileName.Text = fileTransfer;
StorageFile PassedFile = await StorageFile.GetFileFromPathAsync(fileTransfer);
I'm wondering if this is the best way to pass the file between pages? I'd rather not require the user to provide broad system access to the app if possible. Any help you can provide is most appreciated!
The best and most standard way in C#/WPF/UWP way is to use a standard pattern that consist of a general ViewModel class (which contains all the common app data that you want to use in the logic layer), put as a field in the static MainPage (or even in the App.xaml.cs class).
I always do it like this:
1) I use the MainPage automatically created as the "shell" of the app, with a property that is the AppViewModel.
The MainPage (and thus the AppViewModel) can be accessed from everywhere in the app, by setting itself as a static field in its own class (the "Current" static field can be called from everywhere in the app... even in a MessageDialog class!).
This is the code for the MainPage (or a shell Page that you wish, but I suggest doing like this, it is a pretty standard way used even by Microsoft), simpler than you think:
public sealed partial class MainPage : Page
{
public AppViewModel ViewModel { get; set; } = new AppViewModel();
public static MainPage Current { get; set; }
public MainPage()
{
this.InitializeComponent();
Current = this;
}
}
THIS is the trick: to make the page static in one field in its
own class, so that that static field will be UNIQUE in the entire app
(this is one of the main features of the "static" word) and, thus, by calling
MainPage.Current.ViewModel you can immediately get any data (in your
specific case, a StorageFile) stored there.
2) The AppViewModel itself is a class that must implement the INotifyPropertyChanged interface, in order to enable bindable properties and functions.
It is common, among Windows developers, to create a base class that implements it and then derive all the classes that needs bindable (i.e. observable) properties from it.
Here it is, exactly how Microsoft itself creates it:
public class BaseBind : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetProperty<T>(ref T storage, T value,
[CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
}
Then you derive AppViewModel class (and all the other model and viewmodel classes) from it… populating it with all the common properties that you need to share across pages.
I have even added a derived property, in order to show how you can share even multiple data types at once, and a function:
public class AppViewModel : BaseBind
{
public AppViewModel()
{
// Usually we initialize all the starting data here, in the viewmodel constructor...
}
// All common app data
private string sampleCommonString;
public String SampleCommonString
{
get { return sampleCommonString; }
set { SetProperty(ref sampleCommonString, value); OnPropertyChanged(nameof(SampleDerivedProperty1)); OnPropertyChanged(nameof(SampleDerivedProperty2)); }
}
public String SampleDerivedProperty1 => "return something based on SampleCommonString";
public String SampleDerivedProperty2
{
get
{
// evaluate in some way SampleCommonString...
return "Same thing as SampleDerivedProperty1, but it allows to add more than just one istruction";
}
}
// This is a property that you can use for functions and internal logic… but it CAN'T be binded to the UI directly
public String SampleNOTBindableProperty { get; set; }
public void SampleFunction()
{
// Insert code, that needs to interact with all the data contained in the viewmodel itself, here...
// The function has to be with NO parameters, in order to work with simple {x:Bind} markup.
// If your function has to access some specific data, you can create a new bindable (or non) property, just as the ones above, and memorize the data there.
}
}
3) Then, in order to access all this from another Page, just create an AppViewModel field in that page, referencing the viewmodel contained in the static mainpage:
public sealed partial class SecondPage : Page
{
public AppViewModel ViewModel => MainPage.Current.ViewModel;
public SecondPage()
{
this.InitializeComponent();
}
}
...and you can easily bind XAML controls properties to the AppViewModel itself:
<TextBlock Text="{x:Bind ViewModel.SampleCommonString, Mode=OneWay}"/>
<TextBox Text="{x:Bind ViewModel.SampleCommonString, Mode=TwoWay}"/>
<Button Content="Sample content" Click="{x:Bind ViewModel.SampleFunction}"/>
(Mode=OneWay is for real-time binding, in order that the property is immediately updated even in the UI, while Mode=TwoWay is used for those properties that can be edited from the control itself, by the user, in order to interact with app logic).
In this mode you will be able to display data and all its changes in real-time!
So... this is the way to keep all the app data at run-time in a
correct and flexible way... by learning it and practicing, in the
future you will use this pattern even in a smarter way, by creating
viewmodels for every object of your application (for example: if
your app need to store your company's customers data, you will have a
"CustomerViewModel" class derived from the BaseBind class, with all
the data of a customer in it) and creating lists like
ObservableCollection<SampleViewModel> to store all of them (ObservableCollection<t> is a collection type that has built-in mechanism to handle list changes, like adding, removing and reordering list items).
Then you will link every observable collection to the ItemsSource property of a control that inherits from ListBase class (tipically: ListView or GridView), creating a DataTemplate to display each list item, like in this example:
<Page
xmlns:vm="using:SampleApp.ViewModelsPath"
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.SampleListOfObjectViewModel, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:SampleObjectViewModel">
<StackPanel>
<TextBlock Text="{x:Bind SampleObjectProperty1, Mode=OneWay}"/>
<TextBlock Text="{x:Bind SampleObjectProperty2, Mode=OneWay}"/>
<Button Click="{x:Bind SampleObjectFunction}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
...and all the data displayed will be updated in real-time whenever you change it!
Hope this all will help you boost your knowledge about how preparing a WPF/UWP logic layer, because all of this works pretty in the same way even for the WPF apps (i.e. the old desktop programs).
Best regards
There are some other ways to implement your requirement about accessing the same file on different pages. But for your scenario, you could use Future-access list in your UWP app.
By picking files and folders, your user grants your app permission to access items that might not be accessible otherwise. If you add these items to your future-access list then you'll retain that permission when your app wants to access those items again later.
Here is the sample code I made
In the first page:
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add("*");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
// add file to the Future Access list
var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
// this token is the key to get the file.
string FALToken = storageItemAccessList.Add(file, "mediaFile");
// in your real scenario, you need to save the token and pass it when you nee
this.Frame.Navigate(typeof(TestPage), FALToken);
}
In the second page:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
string token = (string)e.Parameter;
var storageItemAccessList = StorageApplicationPermissions.FutureAccessList;
StorageFile retrievedFile = await storageItemAccessList.GetFileAsync(token);
}
So you don't need the broad file system access if you use Future-access list to keep the permission of files.
For more detailed information, please refer to this document: Track recently used files and folders
I'm fairly new to Xamarin and stumbled across MVVM and really like it as an architectural pattern. However, I found that most HowTo's and tutorials out there only address the VVM (i.e. View-ViewModel) side of things, probably for simplicity sake!?
I would like to know how the communication between a ModelView and its associated models takes place using the INotifyPropertyChanged paradigm and other things.
If I understand correctly, I personally would put stuff like data handling, data storage (collections), db connections and stuff like that into a model. At least this is how I would've been doing it in the good old MVC days. Following questions arouse in my mind:
Where do I create the model(s) and how do I assign them to ViewModels?
How do I properly connect Model and ViewModel such that property updates are propagated and can be handled correctly?
Would you set the model as a member of the ViewModel?
In my current example, I would like to implement a SensorModel which provides several sensory data which layers above can subscribe to. I would like to send updates whenever new sensor data is available to the layers above; i.e. a ViewModel, for instance.
I'd basically had something like this in mind:
class Sensor
{
int _id { get; set; }
string _name { get; set; }
}
class SensorModel
{
private List<Sensor> _sensors { get; set; }
public void addSensor(Sensor s) ...
public void removeSensor(Sensor s) ...
}
Does anybody have links to actual/complete MVVM examples, including the connection between Model and ViewModel?
Any help appreciated.
Use Lastest stable Xamarin Forms
MODELS
In the Project, create a Models folder
To store data, i usually use SQLite or a temp store:
class DataStore
{
public static List<SensorModel> SensorStore { get; set; }
}
Create the SensorModel model
class SensorModel
{
internal int Id { get; set; }
internal string Sensor { get; set; }
}
VIEWMODELS
In the Project, create a ViewModels folder
Create a SensorVM viewmodel
class SensorVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public System.Windows.Input.ICommand StartCommand { get; set; }
public string SensorName { get; set; }
public SensorVM()
{
DataStore.SensorStore = new List<SensorModel>();
StartCommand = new Xamarin.Forms.Command(StartSubmit);
}
private void StartSubmit(object paramter)
{
var sensor = new SensorModel()
{
Id = 1,
Sensor = SensorName
};
AddSensor(sensor);
}
public void AddSensor(SensorModel sensor)
{
//do something
DataStore.SensorStore.Add(sensor);
}
}
VIEWS
In the Project, create a Views folder
Create a Sensor.xaml view
<ContentPage.Content>
<StackLayout Spacing="10" Orientation="Vertical">
<Entry Text="{Binding SensorName}" />
<Button Command="{Binding StartCommand}" Text="Start" />
</StackLayout>
</ContentPage.Content>
In the code behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Sensor : ContentPage
{
SensorVM vm;
public Sensor()
{
InitializeComponent();
BindingContext = vm = new SensorVM();
}
}
Hope that helps.
I would like to know how the communication between a ModelView and its
associated models takes place using the INotifyPropertyChanged
paradigm and other things.
I think the best way to create a communication in MVVM is Messaging Center.
https://learn.microsoft.com/pt-br/xamarin/xamarin-forms/app-fundamentals/messaging-center
It's not coupled from device (sensor) code to view models ...
Your messages, in this model, active events that could acess your viewmodels as well as other structures.
A sample of this
In your view use :
public void MessegingCenterInit()
{
#region Bluetooth
MessagingCenter.Subscribe<string, string>("App", "Status_name", (sender, arg) =>
{
App.PVM.Name = $"{arg}";//using INotifyPropertyChanged and view model
viewmodelMethod();//using only a viewmodel
});
#endregion
}
in your model use:
public string Name
{
get { return name; }
set
{
name = value;
App.PVM.Add_patient.AddCanExecuteChanged();//PVM is a viewmodel
//The view model need to have INotifyPropertyChanged as a interface
}
}
In specific code you have (into a generic method or event):
string new_name = John;
MessagingCenter.Send<string,string>("App","Status_name",new_name);
There are several ways to do it, its a simple one, you can try use objects as sender with less information.
Regards
Xamarin itself gives a really good example with their default Master-Detail Solution.
Just create a new Xamarin.Forms App and select the Master-Detail Layout.
It includes several Views, ViewModels (with the BaseVIewModel) and some MockUp Data Classes.
For a start just have a look around there :)
In almost all cases there is no communication between the Model and ViewModel, and very rarely there is communication between the Model and View. If you need to communicate between Model and ViewModel it is extremely likely that you are doing something wrong.
To explain, your model usually describes some entity, like that you have the class Cat:
public class Cat
{
public string Color {get; set;}
}
It is generally used in ViewModel either as the field or as a Collection like:
public class CatsViewModel
{
public List<Cat> Cats {get; set;}
}
The cat shouldn't be able to update by itself, if it is updated it is done either by bindings with the view or somewhere from ViewModel.
So you have some architectural problems in your app, I think.
Beginner here - I'm struggling to wrap my head around accessing viewmodel properties. In my particular case, I have a solution containing two different projects. One contains the UI components, the other the majority of the "work."
My viewmodel in my UI project contains the following property:
private int _createfileprogress { get; set; }
public int CreateFileProgress
{
get { return _createfileprogress ; }
set
{
if (value == _createfileprogress )
return;
_createfileprogress = value;
OnPropertyChanged("CreateFileProgress");
}
}
The purpose of this int property is to fill in the progress of a progress bar. I'd like to reference it in my other project, where a long-running method is executed as such:
public void LongRunningMethod()
{
CreateFileProgress= 0;
// Do some calculations
CreateFileProgress= 10
// More calculations and external calls
CreateFileProgress= 20
// etc.
}
But I can't find the right way to connect these two. I'm sure doing this wrong - would appreciate any guidance. Thanks!
I hope i understand your question.
Your ViewModels and the View are in the same Project und you want to monitor the progress from the Model which is in an another Project?
i think you are searching for Events / Observer Pattern
In MVVM the Model didn't care about the ViewModel and the View.
The Prober Way in MVVM is that the model raise Events which the Viewmodel can subscribe.
General Event Example in .Net
You can create an EventArgs Class like
public class ProgressEventArgs :EventArgs
{
public int CurrentProgress { get; }
public ProgressEventArgs(int progress)
{
CurrentProgress = progress;
}
}
and create the Event in your Model (LongRunningMethod) Class
public event EventHandler<ProgressEventArgs> ProgressChanged;
protected void OnProgressChanged(int progress)
{
ProgressChanged?.Invoke(this, new ProgressEventArgs(progress));
}
So your Method can raise Events
public void LongRunningMethod()
{
OnProgressChanged(0);
// Do some calculations
OnProgressChanged(10);
// More calculations and external calls
OnProgressChanged(20);
// etc.
}
which the ViewModel subscribe
public class ProgressViewModel
{
public ProgressViewModel()
{
var model = new Model();
model.ProgressChanged += (sender, e) => {
//invoke ui thread
Application.Current.Dispatcher.Invoke(
new Action(() =>
{
CreateFileProgress = e.CurrentProgress;
}));
};
}
}
It seems that what might best help you would be to set the DataContext of your UI element (either the Window, the Page, or the ProgressBar itself) to your ViewModel, which will allow your UI to be bound to ViewModel properties. Since your ViewModel seems to implement the INotifyPropertyChanged interface, your OnPropertyChanged() method should automatically keep the UI up-to-date when you use DataBinding.
You'll first want to make a reference to your class library project that contains the "work". This will allow your "UI project" to access the resources (namely, the ViewModel) from the "work project". Do this by right-clicking References under your "UI Project" in the Solution Explorer, choose Add Reference..., find the Projects tab on the left side, then check the box for your "work project". Apply it and your "UI project" can now access your "work project".
Once that is done, you can use either of the following methods to produce the result I believe you're looking for:
Set the Window's DataContext to your ViewModel
(Best for when many elements within your Window will use resources from your ViewModel)
<Window
//Some attributes
//Some more attributes
xmlns:vm="clr-namespace:MyWorkProject.ViewModels;assembly=MyWorkProject">
<Window.DataContext>
<vm:MyViewModel/>
</Window.DataContext>
<Grid>
<ProgressBar Value="{Binding CreateFileProgress}/>
</Grid>
</Window>
OR
Set only the ProgressBar's DataContext using a StaticResources
(Best if you don't need the entire Window's DataContext to be the ViewModel)
<Window
//Some attributes
//Some more attributes
xmlns:vm="clr-namespace:MyWorkProject.ViewModels;assembly=MyWorkProject">
<Window.Resources>
<vm:MyViewModel/>
</Window.Resources>
<Grid>
<ProgressBar Value="{Binding Source={StaticResource MyViewModel}, Path=CreateFileProgress}/>
</Grid>
</Window>
I have been taught lately when using WPF and databinding it is good practice to not name any of the fields but only to associate them with the properties in the other classes. My problem right now is how do I add the data from 3 textboxes (the user enters), save the binded information to the model which then posts the account information into the listbox on the side. I need to add the data to my model. My code from main.xaml is below:
<ListBox ItemsSource="{Binding Path=Files}" SelectedItem="{BindingPath=CurrentItem}" />
<TextBox Text="{Binding Path=bankaccount}"/>
<TextBox Text="{Binding Path=accountnumber}"/>
<TextBox Text="{Binding Path=accounttype}"/>
<Button Content="Save Data To Listbox" Click="Save_Click"/>
Now I will show my FileModel class which holds all of my properties which will be from the textboxes
private short _BankAccount;
private long _AccountNumber;
private char _AccountType;
public short bankaccount{ get { return _BankAccount;} set {_BankAccount= value; Notify("bankaccount"); } }
public long accountnumber{ get { return _AccountNumber;} set {_AccountNumber= value; Notify("accountnumber"); } }
public char accounttype{ get { return _AccountType;} set{_AccountType= value; Notify("accounttype"); } }
I use a class called ProgramModel As my middle point between the Mainpage and my FileModel page and here is that code:
public class ProgramModel : INotifyPropertyChanged
{
public ObservableCollection<FileModel> Files { get; set; }
private FileModel _currentItem;
public FileModel CurrentItem { get { return _currentItem; } set { _currentItem = value; Notify("CurrentItem"); } }
public ProgramModel()
{
Files = new ObservableCollection<FileModel>();
}
And to finish it off I have my mainpage:
internal partial class MainWindow
{
public ProgramModel Model { get; set; }
private ViewSettings _viewSettings = new ViewSettings();
public MainWindow()
{
InitializeComponent();
this.DataContext = Model = new ProgramModel();
}
private void Save_Click(object sender, RoutedEventArgs e)
{
FileModel filemodel = new FileModel();
Model.Files.Add(new FileModel( filemodel.bankaccount, filemodel.accountnumber, filemodel.accounttype));
}
I feel like I am adding to the Files Collection incorrectly from the save button event. If you guys can help me out that would be great! All 3 textboxes and the listbox are on the Main page. Let me know if you have any questions. Also, this is a learning experience so let me know if I posted too much code or not enough. Thanks!
You read the values from a new FileModel instance instead of from what is bound to the view. Code should be this:
Model.Files.Add(new FileModel
(
Model.CurrentItem.bankaccount,
Model.CurrentItem.accountnumber,
Model.CurrentItem.accounttype
));
Make sure CurrentItem is actually initialized with an instance, don't see that in your code. Also, you could use a command here and have all the relevant logic in your bound view model without the need for the event.
Also, right now you bind the current item to the selected item in the ListBox, this will modify an existing instance instead. Not sure if this is intended. If you want those fields to be for input of new instances don't bind the ListBox to it.
I'm not going to answer your question directly because implementing proper data binding will take a bit of code to do so.
Using proper data binding, it is possible to have almost no code behind on your view.cs! (Specially if you start using frameworks)
Please take a look on A Simple MVVM Example for you to follow good practice.
By following this example, you will see that you can also use data binding on buttons and other controls.
Your View Model which is ProgramModel : INotifyPropertyChanged should handle all the work (data processing).
Your model should not handle the UI update notifications thus,
public short bankaccount{ get { return _BankAccount;} set {_BankAccount= value; Notify("bankaccount"); } }
will be moved to the ProgramModel (View Model).
Save_Click method will also be converted into an ICommand and be binded to the button in view like <Button Content="Save Data To Listbox" Command="{Binding SaveExec}"/>
The point is, if you are studying data binding, you should implement it right. Hope you understand...
In the end, it is possible for your Main.cs to only be..
internal partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ProgramModel();
}
}
Just a small change and it should work . Change Your bindings as shown below for the TextBoxes.
<TextBox Text="{Binding Path=CurrentItem.bankaccount}"/>
<TextBox Text="{Binding Path=CurrentItem.accountnumber}"/>
<TextBox Text="{Binding Path=CurrentItem.accounttype}"/>