I am trying to bind the data returned from WCF service to a grid in WPF using MVVM. The same works when i use the logic of WCF service in view model.
Code Behind:
this.DataContext = new SampleViewModel();
View/XAML:
<Window x:Class="Sample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding Students}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID}" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Address" Binding="{Binding Address}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
View Model:
public List<Student> Students {
get {
var service = new StudentServiceClient();
var students = new List<Student>(service.GetStudents());
return students;
}
}
IStudentService:
[ServiceContract]
public interface IStudentService {
[OperationContract]
IEnumerable<Student> GetStudents();
}
[DataContract]
public class Student {
public string Name { get; set; }
public int ID { get; set; }
public string Address { get; set; }
}
StudentService.svc:
public class StudentService : IStudentService {
public IEnumerable<Student> GetStudents() {
var students = new List<Student>();
for (int i = 0; i < 3; i++) {
students.Add(new Student {
Name = "Name" + i,
ID = i,
Address = "Address" + 1
});
}
return students;
}
}
When i run the app, i dont see ant records in the grid..
public List<Student> Students {
get {
var service = new StudentServiceClient();
var students = new List<Student>(service.GetStudents());
return students;
}
}
Every time the Students property is used/read this code will connect to the server and retrieve the students. That will be too slow.
Load the Students in the constructor of the ViewModel (or in a separate method/command) and return this collection from the getter.
The reasons why your solution doesn't work can be:
List doesn't notify the View of changes of the collection; use ObservableCollection instead.
When the Students property changes (var students = new List<Student>(service.GetStudents());) there is no signal to the View that the property has changed; Implement INotifyPropertyChanged on the ViewModel.
Make sure the Service returns data.
Are there any binding errors? Or maybe there is a serviceside problem, and the service returns no entries. Did you debug / breakpoint the property's getter and check the result?
Related
I want to retrieve data from azure mobile service using this code:
//class
class country
{
public string id { get; set; }
public string country_name { get; set; }
public int country_id { get; set; }
}
These are my global variables:
private MobileServiceCollection<country, country> items;
private IMobileServiceTable<country> todoTable = App.MobileService.GetTable<country>();
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
items = await todoTable.Where(todoItem=>todoItem.country_name=="pakistan").ToCollectionAsync(;
mygridview.ItemsSource = items;
}
// mydridview is the name of gridview.
mygridview.ItemsSource = items;
But it only Show azureapp.code.country but not the data.
Note: azureapp is the name of application and country is the name of class and also the name of table in azure.
Your code here above is good, it should actually works.
I think that actually you are displaying in your XAML view the object pointer and not a field. Could you please try to refer to an attribute of the class "Country" in your gridview code?
Thank you MandrX i will be forever in your debt. When i change the gridview to listView your binding technique worked. Thanks again.
<ListView Name="mylistview" Margin="27,299,41,62">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0, 20, 0, 0">
<TextBlock Text="{Binding country_name }" />
<TextBlock Text="{Binding country_id}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I am trying to learn XAML with WINRT and I have a question about binding and viewmodels.
I have an two classes EmployeeList and DownloadableEmployee:
public sealed class EmployeeList
{
public IReadOnlyList<DownloadableEmployee> DownloadableEmployees { get; }
}
public sealed class DownloadableEmployee
{
public System.Boolean IsSelected { get; set; }
public System.String Name { get; }
//method
public IAsyncOperation<IRandomAccessStreamWithContentType> GetEmployeePicAsync();
}
I have binded the EmployeeList to a Multi Selectable ListView displaying employee Names and Pictures. A user can now select / deselect individual employee in the ListView.
To achieve that, I've have created a DownloadableItem class that acts as a ViewModel which is binded to my listview EmployeeLV.
public class DownloadableItemVM
{
public string Name
{
get;
private set;
}
public IRandomAccessStreamWithContentType EnmployeePic
{
get;
private set;
}
public DownloadableItem(string name, IRandomAccessStreamWithContentType thumbnail)
{
Name = name;
Thumbnail = thumbnail;
}
}
ObservableCollection<DownloadableItemVM> employeesToDownload = new ObservableCollection<DownloadableItemVM>();
// set source to ListView
EmployeeLV.ItemsSource = employeesToDownload;
foreach (DownloadableEmployee item in EmployeeList.DownloadableEmployees)
{
IRandomAccessStreamWithContentType stream = await item.GetEmployeePicAsync();
employeesToDownload.Add(new DownloadableItemVM(item.Name, stream));
}
My List View
<ListView x:Name="employeeLV" SelectionMode="Multiple" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Thumbnail}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Question XAML experts:
My ListView already binds to my custom View Model class DownloadableItemVM and properly displays the name and photo of each employee.
But now I have a problem because I need to "bind" each selected item in My ListView back to DownloadableEmployee.IsSelected such that when a user selects an employee, their "IsSelected" gets set to true and vice versa when they deselect.
How should I go about doing this?
Why not to have all DownloadableEmployee object inside your DownloadableItemVM, and bind to name, picture, etc. through this object ? E.g.
public class DownloadableItemVM
{
...
public DownloadableEmployee Employee
{
get;
private set;
}
...
public DownloadableItem(DownloadableEmployee employee, IRandomAccessStreamWithContentType thumbnail)
{
Employee= employee;
Thumbnail = thumbnail;
}
...
}
<ListView x:Name="employeeLV" SelectionMode="Multiple" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Thumbnail}"/>
<TextBlock Text="{Binding Employee.Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
With this approach you can reach your IsSelected prop through Employee object. Is this suitable for you ?
I am binding a model to my Xaml code and have a question about how to bind to a Property.
Let's assume my View Model looks like
internal class LogsVM
{
private List<Log> logList;
public List<Log> LogList
{
get; set;
}
public LogsVM()
{
}
public LogsVM(List<Logging.Log> logs)
{
logList = logs;
}
}
and assume my Log class looks like
internal class Log
{
public string Title { get;set; }
public List<MoreDetails> moreDetails;
public Log()
{
moreDetails= new List<MoreDetails>();
}
}
In Xaml, how do I bind to the Title within a TreeView?
My Xaml so far looks like
<Window x:Class="BackUps.Logging.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myData ="clr-namespace:BackUps.Logging.ViewModel"
Title="Logging Results" Height="350" Width="525">
<Grid>
<Grid.Resources>
<myData:LogsVM x:Key="Vm" />
</Grid.Resources>
<Grid.DataContext>
<Binding Source="{StaticResource Vm}"></Binding>
</Grid.DataContext>
<TreeView>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type myData:LogsVM}" ItemsSource="{Binding LogList}">
<TextBlock Text="{Binding Title}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type myData:LogsVM}">
<TextBlock Text="{Binding moreDetails.Staus}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
And my MainWindow code behind
public MainWindow(List<Log> logs)
{
InitializeComponent();
LogsVM logVm = new LogsVM(logs);
this.DataContext = logVm;
}
As you can see in the above code, I'm trying to bind the Title property but my screen doesn't display an text at all.
So, my 2 questions are:
Is it enough to use my ViewModel class alone or do I also need to tell the Xaml each internal class of the ViewModel (in this case, the Log class)? EG
xmlns:myData ="clr-namespace:BackUps.Logging.ViewModel"
xmlns:moreData = "clr-namespace:BackUps.Logging.Logs"
What do I need to do to bind the Title?
Binding is not complicated as you might think, Your are just not mastering the Treeview's HierarchicalDataTemplate stuff and exposing properties to the XAML,
set your all your domain classes public cause they are used in public properties.
myData should reference the domain classes namespace.for ex: in my case xmlns:myData="clr-namespace:WpfApplication3" MoreDetails has to be a public property in Log class.
<TreeView ItemsSource="{Binding LogList}" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type myData:Log}" ItemsSource="{Binding MoreDetails}">
<TextBlock Text="{Binding Title}" />
<HierarchicalDataTemplate.ItemTemplate >
<DataTemplate DataType="{x:Type myData:MoreDetails}" >
<TextBlock Text="{Binding Status}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
public class Log
{
public string Title { get; set; }
public List<MoreDetails> MoreDetails { get; set; }
public Log()
{
MoreDetails = new List<MoreDetails>();
}
}
public class MoreDetails
{
public string Status { get; set; }
}
public class YourVM
{
public YourVM() // in my case i've just run it fast in code behind
{
LogList = new List<Log>
{
new Log{Title = "Hichem", MoreDetails = new List<MoreDetails>{ new MoreDetails{Status = "OK"}}},
new Log{Title = "Hichem"},
new Log{Title = "Hichem"},
new Log{Title = "Hichem"},
};
}
public List<Log> LogList { get; set; }
}
The program works fine and doesn't crashing or something. But data is not showing on the table(datagrid)
Updated version:
View: Userperspective.xaml
I am getting errors in xaml file because of the binding path "Products" is unkown datacontext
<Grid Margin="0,0,0,-20">
<DataGrid Name="Producttable" ItemsSource="{Binding Path=Products}"
HorizontalAlignment="Left" Height="200" Margin="10,44,0,0"
VerticalAlignment="Top" Width="972" />
View: Userperspective.xaml.cs
public partial class Userperspective : Window
{
public Userperspective()
{
InitializeComponent();
DataContext = new ProductViewModel();
}
}
ProductviewModel
private readonly Product _product;
private IBackend _backend;
public ICommand ProductCommand { get; set; }
public IList<Product> Products { get; set; }
public ProductViewModel()
{
_backend = new BackendService();
_product = new Product();
ProductCommand = new ProductCommand(this);
}
public Product Product()
{
return _product;
}
public void LoadProducts()
{
Products = _backend.GetProducts();
RaisePropertyChanged("Products");
}
Productcommand
private readonly ProductViewModel _vm;
public ProductCommand(ProductViewModel vm)
{
this._vm = vm;
}
public void Execute(object parameter)
{
_vm.LoadProducts();
}
BackendService
namespace _blabla
{
class BackendService : IBackend
{
public IList<Product> GetProducts()
{
using (var db = new NORTHWNDEntities())
{
var query = from p in db.Products
select new Product
{
Name = p.ProductName,
};
return query.ToList();
}
}
}
}
Ibackend
namespace _blabla.Commands
{
public interface IBackend
{
IList<Product> GetProducts();
}
}
Seeing as you are new to WPF and MVVM you should break the problem down into something a little more manageable. There is a lot going on in your code; MVVM, commands, database access and some abstraction. Your intentions are sound but it doesn't make solving this problem easy.
With the information you have given I'm not even 100% sure what the problem is but I suspect that it is either the binding or the database access. I will concentrate on demonstrating the binding aspect to you.
Seeing as I don't have access to your database code I have mocked up some classes to help me solve this problem.
Note: The command code is noise so I will remove it from my answer and concentrate on binding to a list of products (you can integrate it with your commanding solution once this is working).
Product
public class Product
{
public string Name { get; set; }
public string Description { get; set; }
public override string ToString()
{
return string.Format("Product: ({0}), {1}", Name, Description);
}
}
BackendService : This basically returns an array of products in lieu of being able to access a database.
class BackendService : IBackend
{
public IList<Product> GetProducts()
{
return new Product[]
{
new Product{ Name = "Laptop", Description = "Dell 17inch laptop" },
new Product{ Name = "Mobile Phone", Description = "iPhone" },
new Product{ Name = "Television", Description = "Samsung 32 inch plasma" },
new Product{ Name = "Car", Description = "Gran Torino" },
new Product{ Name = "Book", Description = "Effective C#" },
};
}
}
I have bound the list of products in the viewModel to a Listbox as I don't have access to the DataGrid but otherwise I have not modified the main window code.
Mainwindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Margin="5"
ItemsSource="{Binding Path=GetProducts}"/>
</Grid>
Mainwindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ProductViewModel();
}
}
Now If I use your viewModel, I get a NullReferenceException which originates from your call to _backend.GetProducts() because you have not instantiated an instance of your BackendService. If I update the constructor like so:
public ProductViewModel()
{
_backend = new BackendService();
_product = new Product();
ProductCommand = new ProductCommand(this);
}
and run the application, the list of products is displayed correctly.
You should be able to integrate the code I have supplied into your project and demonstrate that it is working. When you are happy with this, you should update the BackendService class to call the list of products from your database instead. I would recommend doing this as a matter of course for all bindings that way you know whether it is the binding that isn't working or the database call.
You are trying to execute GetProducts but that is a Property not a method - create a seperate method to load products and
Change your property name to something more meaningful
public IList<Product> Products {get;set;}
Then create a method to load your products
public void LoadProducts()
{
Products = _backend.GetProducts();
//You will need to notify of property change here
OnPropertyChanged("Products");
}
Then bind to Products in your xaml
<Window x:Class="_blabla.View.Userperspective"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UserPerspective" Height="500" Width="1000">
<Grid Margin="0,0,0,-20">
<DataGrid Name="Producttable" ItemsSource="{Binding Path=Products}"
HorizontalAlignment="Left" Height="200" Margin="10,44,0,0"
VerticalAlignment="Top" Width="972" />
</Grid>
</Window>
Then in your command call LoadProducts
public void Execute(object parameter)
{
_vm.LoadProducts();
}
You will need to implement INotifyPropertyChanged so the UI knows you have changed the Products Property
How can I get DesignTime data in WinRT XAML so the designer shows sample data?
Simple enough.
Create a Model like this:
public class Fruit
{
public string Name { get; set; }
}
Create a base ViewModel like this:
public class BaseViewModel
{
public ObservableCollection<Fruit> Fruits { get; set; }
}
Create a real ViewModel like this:
public class RealViewModel : BaseViewModel
{
public RealViewModel()
{
if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
LoadData();
}
public void LoadData()
{
// TODO: load from service
}
}
Create a fake-data ViewModel like this:
public class FakeViewModel : BaseViewModel
{
public FakeViewModel()
{
this.Fruits = new ObservableCollection<Fruit>
{
new Fruit{ Name = "Blueberry"},
new Fruit{ Name = "Apple"},
new Fruit{ Name = "Banana"},
new Fruit{ Name = "Orange"},
new Fruit{ Name = "Strawberry"},
new Fruit{ Name = "Mango"},
new Fruit{ Name = "Kiwi"},
new Fruit{ Name = "Rasberry"},
new Fruit{ Name = "Blueberry"},
};
}
}
Do this in your XAML:
<Page.DataContext>
<local:RealViewModel />
</Page.DataContext>
<d:Page.DataContext>
<local:FakeViewModel />
</d:Page.DataContext>
Have fun!
PS: you can also attempt to use d:DesignData.
That approach also works. I feel it is not as straight forward.
In the end, it's up to you how to do it.
Either way, don't miss out on DeisgnTime data!
Here is the d:DesignInstance sample:
I will also use Jerry's Fruit class, but I won't use MVVM here as you don't need that to make it works.
Basically, we need to create the data model class (e.g., ViewModel or Model) that we want to have design data (e.g., in this case, I create a child class, but you don't need to).
public class Fruit
{
public string Name { get; set; }
}
public class SampleFruit : Fruit
{
public SampleFruit()
{
Name = "Orange (Sample)";
}
}
Then in our XAML, we can use d:DataContext to bind the child class.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding}"
d:DataContext="{Binding Source={d:DesignInstance Type=local:SampleFruit, IsDesignTimeCreatable=True}}">
<TextBlock Text="{Binding Name}"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="42"/>
</Grid>
Please note this line:
d:DataContext="{Binding Source={d:DesignInstance Type=local:SampleFruit, IsDesignTimeCreatable=True}}"
Now you should see your design time data on both Visual Studio Designer and Blend.
P.S. In Blend 2013, there is a data tab that let you create sample data as well.