 .
 . Choosing the structure of the application, I focus primarily on the logical separation of the functions performed by projects. Based on these considerations, to create a database and all data types, we create a separate project, Entities, do not forget to add System.Data.Linq and Microsoft.Practices.Prism to References. Solution here .
 Choosing the structure of the application, I focus primarily on the logical separation of the functions performed by projects. Based on these considerations, to create a database and all data types, we create a separate project, Entities, do not forget to add System.Data.Linq and Microsoft.Practices.Prism to References. Solution here .[Table(Name = "Transactions")] public class Transaction : NotificationObject  [Table(Name = "Categories")] public class Category : NotificationObject  public class Database : DataContext { private static string DBConnectionString = "Data Source=isostore:/Database.sdf"; public Database() : base(Database.DBConnectionString) { this.DeferredLoadingEnabled = true; } public Table<Bick.Budget.Entities.Categories.Category> Categories; public Table<Bick.Budget.Entities.Transactions.Transaction> Transactions; } Do not even think of making tables in the database properties and not fields. I, because of the stylistic habit of using properties for public, killed about an hour trying to understand why the database is not working at all.
 :
 : [Column(IsPrimaryKey = true)] public Guid ID { ... } ... private EntityRef<Categories.Category> category; [Association(Name = "FK_Transactions_Category", Storage = "category", ThisKey = "CategoryID", IsForeignKey = true)] public Categories.Category Category { get { return this.category.Entity; } set { Categories.Category previousValue = this.category.Entity; if (((previousValue != value) || (this.category.HasLoadedOrAssignedValue == false))) { if ((previousValue != null)) { this.category.Entity = null; previousValue.Transactions.Remove(this); } this.category.Entity = value; if ((value != null)) { if ((value.AddedTransactions == null) || (!value.AddedTransactions.Contains(this))) { value.Transactions.Add(this); } this.CategoryID = value.ID; } else { this.category = new EntityRef<Categories.Category>(); } this.RaisePropertyChanged(() => this.Category); } } } The ID parameter is a table column and primary key. The remaining columns are also specified by the Column attribute. More details about Attribute-based mapping can be read on msdn . :
 : public EntitySet<Transactions.Transaction> Transactions { get { if (this.transactions == null) { this.transactions = new EntitySet<Transactions.Transaction>(); this.transactions.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(this.OnTransactionsChanged); } return this.transactions; } ... } ... private void OnTransactionsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { this.AddedTransactions = e.NewItems; foreach (Transactions.Transaction t in e.NewItems) { t.Category = this; } this.AddedTransactions = null; this.RaisePropertyChanged(() => this.Transactions); } As a matter of fact, in Category we catch the situation when no category is assigned to a transaction, and a new item is added to the category transaction list.
 We will divide these screens into the following View: New / Edit transaction, New / Edit category, Categories list, Transactions list, and the part responsible for working with transactions is transferred to a separate project. Solution on SkyDrive .
 We will divide these screens into the following View: New / Edit transaction, New / Edit category, Categories list, Transactions list, and the part responsible for working with transactions is transferred to a separate project. Solution on SkyDrive . public Database(bool isReadOnly = false) : base(Database.DBConnectionString) { if (!this.DatabaseExists()) { this.CreateDatabase(); } this.DeferredLoadingEnabled = true; this.ObjectTrackingEnabled = !isReadOnly; } Thus, with isReadOnly == true, we disable tracking of context objects for changes, which on average increases the speed of simple reading by more than 10 times. :
 : <phone:PhoneApplicationPage xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:appbar="clr-namespace:AppBarUtils;assembly=AppBarUtils"> … <phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="False" Mode="Default"> <shell:ApplicationBarIconButton IconUri="/icons/appbar.add.rest.png" Text="add new"/> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar> <i:Interaction.Behaviors> <appbar:AppBarItemCommand Id="add new" Command="{Binding Path=AddCategoryCommand}"/> </i:Interaction.Behaviors> Most often, the actions of the buttons will be transitions to other pages of the application and we need to make a convenient mechanism for working with navigation from the ViewModel. A convenient and relatively familiar (I once worked with the desktop MVVM on a similar principle) the method is described here . A similar principle we implement in our Common project by creating the ApplicationController class. Also all our View's will be defined in the static KnownPages class: public static class KnownPages { public const string AddCategory = "/Views/CategoryAddEditView.xaml?mode=Add"; public const string EditCategory = "/Views/CategoryAddEditView.xaml?mode=Edit&id={0}"; public const string ListCategories = "/Views/CategoriesView.xaml"; public const string CategoryDetails = "/Views/CategoryDetailsView.xaml?id={0}"; } , a NavigateTo () from ApplicationController (still a little left of the original) will look like this public void NavigateTo(string url, params object[] data) { Uri address = new Uri(String.Format(url, data), UriKind.Relative); PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame; root.Navigate(address); } Now, since we are passing the mode = Add parameter to the AddEdit page, we need to catch the navigation event in the ViewModel and get the data from the string. Unfortunately, at the moment I stopped at the option of overriding the OnNavigatedTo method in CodeBehind and calling the corresponding method in the ViewModel. protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); ((CategoryAddEditViewModel)this.DataContext).OnNavigatedTo(this.NavigationContext, this.NavigationService.BackStack.First()); } As can be seen from the code, we transfer not only the navigation context (from where it is convenient to pull the parameters from the page address), but also the page from which we switched to the current one. xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Interactivity;assembly=Microsoft.Practices.Prism.Interactivity" ... <TextBox TextWrapping="Wrap" Text="{Binding Path=Category.Name, Mode=TwoWay}"> <i:Interaction.Behaviors> <prism:UpdateTextBindingOnPropertyChanged /> </i:Interaction.Behaviors> </TextBox> The process of category preservation looks like this: public void SaveCategory() { if (!this.isEditMode) { this.model.AddCategory(this.Category); ApplicationController.Default.GoBack(); } } What you should pay attention to - we do not go to the CategoriesView page, but return to the previous page. It is worth paying attention to similar transitions within the application so that the user is not at a loss where he suggested after clicking on the Back button. public void AddCategory(Category cat) { using (Database db = new Database()) { db.Categories.InsertOnSubmit(cat); db.SubmitChanges(); } } It can be seen that there are no checks and validations - and this is bad. But for the first article there is already quite a lot of material, and now it is more important for us to finish the main functionality and start using the program - we'll do the rest between the articles or in the following. private void AddCategory() { this.isReloadPending = true; ApplicationController.Default.NavigateTo(KnownPages.AddCategory); } public void OnNavigatedTo(NavigationContext context, JournalEntry lastPage) { if (this.isReloadPending) { this.isReloadPending = false; this.Categories = this.model.GetCategoriesList(); } } 
Source: https://habr.com/ru/post/138117/
All Articles