I'm going to do a wpf application using MVVM(It based on http://www.codeproject.com/KB/WPF/MVVMQuickTutorial.aspx
).
This application will be connecting with webservice one per month.
On webservice I have contract
public class Student
{
public string Name {get; set;}
public int Score {get; set;}
public DateTime TimeAdded {get; set;}
public string Comment {get; set;}
}
In WPF application Adding, and removing students will be saving to xml file.
So at wpf application Student would be something like :
public class Student
{
public string Name {get; set;}
public int Score {get; set;}
public DateTime TimeAdded {get; set;}
public string Comment {get; set;}
public Student(string Name, int Score,
DateTime TimeAdded, string Comment) {
this.Name = Name;
this.Score = Score;
this.TimeAdded = TimeAdded;
this.Comment = Comment;
}
}
public class StudentsModel: ObservableCollection<Student>
{
private static object _threadLock = new Object();
private static StudentsModel current = null;
public static StudentsModel Current {
get {
lock (_threadLock)
if (current == null)
current = new StudentsModel();
return current;
}
}
private StudentsModel()
{
// Getting student s from xml
}
}
public void AddAStudent(String Name,
int Score, DateTime TimeAdded, string Comment) {
Student aNewStudent = new Student(Name, Score,
TimeAdded, Comment);
Add(aNewStudent);
}
}
How connect this two classes ?
The worst think I guess is that contract Student from webservice would be use in this wpf application to get students from xml, in other application collection of studetns would be getting from database.
I'm newbie in design patterns so it is very hard for me :/
Example: I click AddUser, and in application A it calls webservice method which adding user to database, in application B it adds user to XML file, and In application.
Base class are contracts at webservice.
Next explanation:
First application uses webservice to save data at database. Second application never save data in xmls and one perm month send this xmls to webservice and convert their to intances of students and save it at database
It is very unclear from your question what the actual problem is. But, I guess I could address a few and show you a way to solve them.
1) The main problem I see in your project is you have two definitions of Student class. You can easily merge them into a single definition. (I will just show you how...)
2) It is very unclear whether you want your WPF client to save data to a Data Source (XML?) or your Web Service should do it. And if the WPF client is supposed to save the Students then what is the Web Service for?
3) You don't have a ViewModel defined anywhere for the Student class which in this case is Model.
I have created an example with 3 projects.
1) WebService - A WCF Service Project
2) StudentLib - A Class Library Project (where Student class is defined)
3) DesktopClient - A WPF Application Project
Here is the source code :
WebService.IStudentService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using StudentLib;
namespace WebService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IStudentService" in both code and config file together.
[ServiceContract]
public interface IStudentService
{
[OperationContract]
StudentLib.Student GetStudentById(Int32 id);
[OperationContract]
void AddStudent(StudentLib.Student student);
}
}
WebService.StudentService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using StudentLib;
namespace WebService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "StudentService" in code, svc and config file together.
public class StudentService : IStudentService
{
public StudentLib.Student GetStudentById(int id)
{
return new StudentLib.Student() { Name = "John Doe", Score = 80, TimeAdded = DateTime.Now, Comment = "Average" };
}
public void AddStudent(StudentLib.Student student)
{
// Code to add student
}
}
}
WebService's Web.Config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<bindings />
<client />
<services>
<service name="WebService.StudentService" behaviorConfiguration="metaDataBehavior">
<endpoint address="basic" binding="basicHttpBinding" contract="WebService.IStudentService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="metaDataBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
StudentLib.Student.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
namespace StudentLib
{
[DataContract]
public class Student
{
[DataMember]
public String Name { get; set; }
[DataMember]
public Int32 Score { get; set; }
[DataMember]
public DateTime TimeAdded { get; set; }
[DataMember]
public String Comment { get; set; }
}
}
DesktopClient.StudentViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DesktopClient
{
class StudentViewModel
{
protected StudentLib.Student Student { get; set; }
public StudentViewModel(StudentLib.Student student)
{
this.Student = student;
}
public String Name { get { return Student.Name; } }
public Int32 Score { get { return Student.Score; } }
public DateTime TimeAdded { get { return Student.TimeAdded; } }
public String Comment { get { return Student.Comment; } }
}
}
DesktopClient.MainWindow.xaml
<Window x:Class="DesktopClient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="400"
Height="300"
Loaded="Window_Loaded">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0">Name :</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="0"
Text="{Binding Name}"></TextBlock>
<TextBlock Grid.Column="0"
Grid.Row="1">Score :</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="1"
Text="{Binding Score}"></TextBlock>
<TextBlock Grid.Column="0"
Grid.Row="2">Time Added :</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="2"
Text="{Binding TimeAdded}"></TextBlock>
<TextBlock Grid.Column="0"
Grid.Row="3">Comment :</TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="3"
Text="{Binding Comment}"></TextBlock>
</Grid>
</Window>
DesktopClient.MainWindow.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DesktopClient.StudentService;
using StudentLib;
namespace DesktopClient
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IStudentService client = new StudentServiceClient();
Student student = client.GetStudentById(1);
DataContext = new StudentViewModel(student);
client.AddStudent(new StudentLib.Student() { Name = "Jane Doe", Score = 70, TimeAdded = DateTime.Now, Comment = "Average" });
}
}
}
Here all the above mentioned problems are resolved :
1) The Student class is defined in a common assembly (StudentLib) referenced by both WebService project and DesktopClient project. So, while adding a Service Reference, that class is reused by the code generator.
2) I recommend all the storage related operations be in the Web Service and the Client app should just use Web Service to store data.
3) StudentViewModel class is used instead of Student class to display data in MainWindow.
You can use the following architecture.
1) WPF Application (Application A & B) with a app.config which contains either Web Service URL or XML file path
2) Business/Data Layer
Create a proxy class for the Web service URL and create the Student Model class which you mentioned in your post.
Modify your Add method to support both functionalities. Call Add method in Webservice if app.config has webservice url (App A) or call add to XML method if appsetting has XML filepath(App B)
Fortunately, the classes are already "linked", at least in form: because the ViewModel is just a bunch of Models.
In the MVVM pattern, you'll want to handle your data-binding functions in the ViewModel. This includes the following:
The StudentModel constructor (private StudentsModel() { ...) should load itself with all the Student instances. (Alternately, you could use a separate Load() function--which seems most logical if you have a Save() method as well.) Here, you would presumably read the XML file, use an XmlSerializer to deserialize the XML data into a collection of students (probably List), then add them to itself using either the base constructor (for the whole list) or the Add() method (one at a time, e.g. in a loop).
You would need functions to add Students to the collection, as in the tutorial example. On the desktop app, here is where you want to call the Add function on the web service, and if it's successful you add it to your own collection in memory. You then have to decide if you want to immediately save the (serialized) data into the XML file, or do it all together later (for example, when unloading the object or calling a Save() method).
You probably want to include a method to remove Student objects from the collection, too. The public methods of the ObservableCollection will be some help here, but you may want/need more logic than that in choosing which object(s) to remove. And again, here you must notify the web service about the deleted items, and know whether you're going to save changes right away or wait for a separate event.
Inheriting from the ObservableCollection is wise, as that gets you a lot of the magic that makes the binding dynamic--I mean, where the UI is updated as the data changes. As long as your using the base methods of Add() and Remove() etc. you are getting the benefits of IEnumerable, INotifyPropertyChanged, and INotifyCollectionChanged.
You may notice I mostly mentioned XML serialization in the explanation above, but for making a web service with similar functionality, you'll swap out ideas like "serialize the XML and save into a file" for "save changes into your database". The pattern is the same, it's just the actual actions (implementation) is different.
In short, the ViewModel here is where you want to implement all the stuff that loads & handles the data in memory and saves it out to the file, database, or web service.
Related
Goal:
I want to display an ImageButton with an image just like this:
The current solution is working but I can only choose to set a single image hardcoded into xaml.
For this purpose I have already prepared multiple things:
Current Situation
I have added the image and set it as "Build Action: Embedded Resource"
I have added ImageResourceExtension just as suggested online for use in xaml:
using System;
using System.Reflection;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Bitconomy.resources
{
[ContentProperty(nameof(Source))]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null)
{
return null;
}
// Do your translation lookup here, using whatever method you require
var imageSource = ImageSource.FromResource(Source, typeof(ImageResourceExtension).GetTypeInfo().Assembly);
return imageSource;
}
}
}
I have added reference to the ImageResourceExtension/resources in xaml:
xmlns:resources="clr-namespace:Bitconomy.resources"
I have added the image resource to the ImageButton:
<ImageButton x:Name="MiningButton" Grid.Row="0" Source="{resources:ImageResource Bitconomy.resources.images.mines.stone_mine.jpg}" ></ImageButton>
The result is the one shown in the top under the Goal section. fine so far.
Issue
I would like to set up the mines on the go. I have prepared a "mining_view.xaml". Within "mining_view.xaml.cs", I have added code to configure the mine, eg. for stone, iron, or whatever it will be. I want to have different images for each mine.
I have had a similar question in the past available on my stack overflow but it was for desktop use. I was shocked how different mobile development is after all. Similar but not the same.
I'd be very happy if you could help me figure out on how to set the image source in code so it will work in both, android and ios.
public void ConfigureMine(string ItemType)
{
this.MiningButton.Source = new ImageSource.FromResource("resources:ImageResource Bitconomy.resources.images.mines.stone_mine.jpg");
// or sth like that?
this.MiningButton.Source = resources.ImageResourceExtension.ProvideValue(SomeProvider?)
}
create an image in xaml. Assign an x:Name property to select the image from code:
<Image x:Name="Mine_Image" Grid.Column="0" Grid.Row="0" Aspect="AspectFill"></Image>
You will need an resource extension:
using System;
using System.Reflection;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Bitconomy.resources
{
[ContentProperty(nameof(Source))]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null)
{
return null;
}
// Do your translation lookup here, using whatever method you require
var imageSource = ImageSource.FromResource(Source, typeof(ImageResourceExtension).GetTypeInfo().Assembly);
return imageSource;
}
public ImageSource GetImageSource(string resourcepath)
{
return ImageSource.FromResource(resourcepath, typeof(ImageResourceExtension).GetTypeInfo().Assembly);
}
}
}
add resource image
Set image to build action "Embedded Resource"
Change Image within form.xaml.cs (adjust the resource path for your needs):
this.Mine_Image.Source = Resources.GetImageSource("Bitconomy.resources.images.resources.Stone_mine.jpg");
====SOLVED====
Joe's answer was spot on and I was able to get this working using his advice. I basically just had to add the effect in my code behind and it worked.
Using information found on the net, I created a PlatformEffect for iOS that can be assigned to any element. It adjusts the view to take into account an iPhone notch, if present.
My problem is, I am unable to reference the platform effect in my XAML.
Theortically, given the code at the bottom of this message, I should be able to use the following to apply the effect:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Enterprise.View.Features.Authentication.LoginView"
xmlns:effect="clr-namespace:Enterprise.iOS.Effects">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentView BackgroundColor="Green">
<ContentView.Effects>
<effect:SafeAreaPaddingEffect />
</ContentView.Effects>
<Label Text="Hello, from XamarinHelp.com" />
</ContentView>
</Grid>
</ContentPage>
However, the effect reference cannot be resolved in the ContentPage declaration. I'm probably doing something wrong, but I'm not sure what it is. I have not found anything via searches that answer my question.
Any thoughts? Here is the PlatformEffect file (located under an Effects folder in the iOS solution):
using Enterprise.iOS.Effects;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ResolutionGroupName("Enterprise.iOS")]
[assembly: ExportEffect(typeof(SafeAreaPaddingEffect), nameof(SafeAreaPaddingEffect))]
namespace Enterprise.iOS.Effects
{
public class SafeAreaPaddingEffect : PlatformEffect
{
Thickness _padding;
protected override void OnAttached()
{
if (Element is Layout element)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
_padding = element.Padding;
var insets =
UIApplication.SharedApplication.Windows[0].SafeAreaInsets; // Can't use KeyWindow this early
if (insets.Top > 0) // We have a notch
{
element.Padding = new Thickness(_padding.Left + insets.Left, _padding.Top + insets.Top,
_padding.Right + insets.Right, _padding.Bottom);
return;
}
}
// Uses a default Padding of 20. Could use an property to modify if you wanted.
element.Padding = new Thickness(_padding.Left, _padding.Top + 20, _padding.Right, _padding.Bottom);
}
}
protected override void OnDetached()
{
if (Element is Layout element)
{
element.Padding = _padding;
}
}
}
}
To implement an effect for usage in a XAML Xamarin Forms project the following classes might be defined:
MyEffects.EffectIds (namespace MyEffects, class name EffectIds) located in a Xamarin Forms or a Netstandard project (e.g. MyProject) to define the identifier of the Effect.
MyEffects.MyEffect (namespace MyEffects, class name MyEffect) located in a Xamarin Forms project or a Netstandard project (e.g. MyProject) to define the Xamarin Forms effect.
MyEffects.iOS.MyEffect (namespace MyEffects.iOS, class name MyEffect) located in an iOS project to implement the iOS effect.
Sample MyEffects.EffectIds:
using Xamarin.Forms;
[assembly: ResolutionGroupName(MyEffects.EffectIds.GroupName)]
namespace MyEffects
{
public class EffectIds
{
public const string GroupName = "MyEffects";
public static string MyEffect => typeof(MyEffect).FullName;
//another effect not defined here
public static string MyOtherEffect => typeof(MyOtherEffect).FullName;
...
Sample MyEffects.MyEffect:
using Xamarin.Forms;
namespace MyEffects
{
public class MyEffect : RoutingEffect
{
public MyEffect() : base(EffectIds.MyEffect) { }
}
}
Sample MyEffects.iOS.MyEffect:
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using PlatformEffects = MyEffects.iOS;
using RoutingEffects = MyEffects;
[assembly: ExportEffect(typeof(PlatformEffects.MyEffect), nameof(RoutingEffects.MyEffect))]
namespace MyEffects.iOS
{
public class MyEffect : PlatformEffect
{
protected override void OnAttached()
{
...
Sample usage in XAML:
<ContentPage
xmlns:effects="clr-namespace:MyEffects;assembly=MyProject"
...
<Entry ... >
<Entry.Effects>
<effects:MyEffect />
</Entry.Effects>
</Entry>
I was able to fix this by assigning the effect in the code behind. So the relevant XAML not looks like the following
<ContentView BackgroundColor="Green">
<Label x:Name="HelloLabel" Text="Hello, from XamarinHelp.com" />
</ContentView>
and in my code behind, I added the followin immediately after initializing the component
HelloLabel.Effects.Add(Effect.Resolve("Enterprise.iOS.Effects.SafeAreaPaddingEffect"));
We are a newbie for Xamarin. We are having an issue in binding the response data from a web service to a ListView.
We debugged and we can see the the web service is successfully responding with the data but it never gets populated.
Any ideas?
It's gotta be a small thing that we are missing. We have managed to display a single entry from the data with other views (in other parts of the project) BUT not in IEnumerable<> or List<>
Here's the code:
View - RoundsPage.xaml :
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:AthlosifyMobile.ViewModels"
x:Class="AthlosifyMobile.Views.RoundsPage">
<ContentPage.BindingContext>
<viewModels:RoundsViewModel />
</ContentPage.BindingContext>
<StackLayout>
<Entry Text="{Binding AccessToken}" />
<Button Command="{Binding GetRoundsCommand}" Text="Get all rounds" />
<Label Text="Rounds: "></Label>
<ListView ItemsSource="{Binding Rounds}" HasUnevenRows="true" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="Round 1:"></Label>
<Label Text="{Binding Name}"></Label>
<Label Text="{Binding DailyHandicap}"></Label>
<Label Text="{Binding PlayedUTC}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>`
ViewModel - RoundsViewModel.cs :
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using AthlosifyMobile.Annotations;
using Xamarin.Forms;
using AthlosifyMobile.Services;
using AthlosifyMobile.Models;
namespace AthlosifyMobile.ViewModels
{
public class RoundsViewModel : INotifyPropertyChanged
{
ApiServices _apiServices = new ApiServices();
public event PropertyChangedEventHandler PropertyChanged;
private IEnumerable<Round> _rounds;
public string AccessToken { get; set; }
public IEnumerable<Round> Rounds
{
get
{
return _rounds;
}
set
{
_rounds = value;
OnPropertyChanged();
}
}
public ICommand GetRoundsCommand
{
get
{
return new Command(async() =>
{
Rounds = await _apiServices.GetRoundsAsync(AccessToken);
});
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Model - Course.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace AthlosifyMobile.Models
{
public class Round : EntityBase
{
public Guid RoundID { get; set; }
public Guid UserID { get; set; }
public Guid RoundCategoryID { get; set; }
public Guid CourseID { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
public int DailyHandicap { get; set; }
public DateTime PlayedUTC { get; set; }
public RoundCategory RoundCategory { get; set; }
public Course Course { get; set; }
public ICollection<RoundHole> RoundHoles { get; set; }
}
public abstract class EntityBase
{
public DateTime CreatedUTC { get; set; }
public DateTime LastModifiedUTC { get; set; }
}
}
Services - apiservices.cs:
using AthlosifyMobile.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace AthlosifyMobile.Services
{
public async Task<IEnumerable<Round>> GetRoundsAsync(string accessToken)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var json = await client.GetStringAsync("http://localhost:5609/api/Rounds");
var list = JsonConvert.DeserializeObject<IEnumerable<Round>>(json);
return list;
}
}
}
You will need to diagnose whether this is an issue with connecting the View to the ViewModel or whether your Data Service isn't working correctly. Either way, there are a few things you should do to fix this!
Firstly you are using IEnumerable, instead you should be using ObservableCollection<T>. You should always be using ObservableCollection<T> for Binded list views. This is explained in the xamarin docs here (they automatically notify the view when their contents changed & update).
So you should make this change:
public ObservableCollection<Round> Rounds { get; }
Next you should verify that the bindings are correct. I would not recommend your approach of going straight to live data if you aren't familiar with xamarin. Firstly you should try adding some static objects to the view model and trying to bind them!
Disconnect your API code and call a method that creates some of your Round objects. Here is an example method (i use methods like these all the time when designing my ListViews UI).
public RoundsViewModel()
{
Rounds = CreateSampleData();
}
private ObservableCollection<Round> CreateSampleData()
{
ObservableCollection<Round> dummyData = new ObservableCollection<Round>();
dummyData.Add(new Round() { Name="User", handicap=1, PlayedUTC=DateTime.Now });
dummyData.Add(new Round() { Name="User", handicap=1, PlayedUTC=DateTime.Now });
dummyData.Add(new Round() { Name="User", handicap=1, PlayedUTC=DateTime.Now });
return dummyData;
}
At this point you will either see items in your ListView, meaning you have an issue with your API code / Implementation of INotifyPropertyChanged. If you don't see anything then you likely have an issue with binding and will need to verify that your view is actually connected to the View Model.
Mvvm Helpers
Seeing some of this code makes me feel very sorry for you, you definitely should looking into using an MVVM helper such as Prism or MVVMCross. I personally use Prism which provides a ViewModelBase which all ViewModels inherit from. This means all of the INotifyPropertyChanged code is hidden away from you (less boilerplate). It also has a dependancy service which means hooking views up to view models is as simple as registering it in the app.cs.
If you are interested in prism, watch this video with Brian Lagunas to see what Prism can do for you!
Update: There are now a few helpful libraries aside from Prism that will help with the MVVM stuff. Refractored.MVVMHelpers and Xamarin.CommunityToolkit both contain an essential object: ObservableRangeCollection.
ALL code using an ObservableCollection should be replaced with ObservableRangeCollection, it is an essential object and really belongs in a microsoft maintained namespace at this point. It creates a performance benefit for updating larger collections & reduces the need for alot of boilerplate when updating the ObservableCollection
Want to see if someone could help clear this up for me? Is there an advantage to DataBinding from the XAML Element to a value within the ViewModel(ex:1), or from the CodeBehind(ex:2) back to the Element like... HostName.Text?
<TextBlock Text="{Binding HostName}" /> --- (ex:1)
<TextBlock Name="HostName" /> --- (ex:2)
POGO
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Task;
namespace AppName.Models
{
public class Contact
{
[Key]
public int Id {get; set;}
public string Team { get; set;}
public string FirstName { get; set;}
public string LastName { get; set;}
public string Phone { get; set;}
public string Email { get; set;}
public string Role { get; set;}
public string DisplayName => $"[LastName}, {FirstName}";
}
}
The simple answer is that it depends on your application and needs. When you're building a small application, using code behind or data binding to view models doesn't make much difference. It's easy to understand the flow and when to make updates. But as your app complexity goes up and your need to test your code, then you start to use patterns that makes your code more maintainable and testable. That's where the MVVM pattern came from.
Testing code in your code behind file is harder than just testing your business logic in your ViewModel class, and ensuring it works as expected.
Your example above is kind of simplistic because it's a TextBlock that only displays text and doesn't take input. A TextBox is a better example for binding since data can change in the view model or in the UI. Binding lets you back the displayed text with a property, so changes from any direction update the model property and UI automatically.
<TextBox x:Name="Entry" Text="{Binding SelectedValue , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
ViewModel:
public class CatalogViewModel : BindableBase
{
private string selectedValue;
public string SelectedValue
{
get { return selectedValue; }
set { SetProperty<string>(ref selectedValue, value); }
}
...
}
The alternative to that is a lot of code in the code-behind file to keep things in sync between the TextBox and data elements.
<TextBox x:Name="Entry2" TextChanged="Entry2_TextChanged" />
Code behind:
private string entryText;
public string EntryText
{
get { return entryText; }
set
{
if (value != entryText)
{
entryText = value;
Entry2.Text = entryText;
}
}
}
private void Entry2_TextChanged(object sender, TextChangedEventArgs e)
{
entryText = Entry2.Text;
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// initialize controls
EntryText = "Default";
}
You've now coupled your business logic with the page layout and changes to either will cause a lot of changes. And testing the behavior of your code is harder to write and harder to mock.
And it just gets more complicated with multiple input controls and more complex controls, like ListViews and GridViews.
You should read up on MVVM if you're interested in the benefits of using view models and databinding to them: https://msdn.microsoft.com/en-us/magazine/dd419663.aspx.
I have created Enum class of LeaveReason in data access layer having values sick leave,planned leave or other reason now i want to call this enum class to my custom type layer but how to call it ???
please help as i am new in c#...
here is my code looks like.....
In Data Access layer :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sherserve.DataAccessLayer
{
public enum LeaveReason
{
Sick,
Planned,
Other
}
}
In Data Custom Type layer :
Now i want to access enum class which i created in data access layer in custom type layer.
You can see i have added reference of data access layer but it is showing error..
please correct this and tell me how i can call enum class in custom type layer.
using System;
using System.Collections.Generic;
using System.Text;
using Sherserve.DataAccessLayer;
namespace Sherserve.CustomTypeLayer
{
public class EmployeeLeave
{
public LeaveReason LeaveType { get; set; }
public int EmployeeId { get; set; }
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public string Reason { get; set; }
}
}
my problem is that i am un able to call enum class from data access to custom type and also i am unable to add reference of data access class to custom type...
please guide me properly..
thanks
You need to add a reference to the Sherserve.DataAccessLayer in the Sherserve.CustomTypeLayer. Typically the Sherserve.CustomTypeLayer would be a class library project that its output is a .dll file that you add to the other dataaccess layer from references -> add reference.