WPF DataGrid Parent Child Data - c#

I'm new to WPF/C#/NET but I have been learning by way of coding some small exercises.
Anyway, I'm stuck. I have seached on here and google and can't find the answer or maybe more correctly can't find an answer I can make sense of.
My problem is this... using the Entity Framework I have two tables. One for Employee details and one for Company details. Employees work for 0 or 1 Company's.
I would like to, via WPF/XAML, define a datagrid to navigate Employees. But within each employee row I would like to show the name of the Company they work for (if there is a relationship) or "Unemployed" in the cases where there is no related Company record.
I have not given details of the tables as it really doesnt matter - the problem is displaying concatentated information from parent/child relationships in a single datagrid.
I dont know what the best approach to this kind of problem is, I'm assuming WPF/DataGrid, so I would really appreciate help on how to go about doing it, the binding (assuming WPF) or even an example of the WPF/XAML
Thanks in advance.

There are many ways to accomplish this - one way you might try is to create a View Model that encapsulates the data you want to display - e.g.
public class EmployeeViewModel
{
private readonly Employee _employee;
public EmployeeViewModel(Employee employee)
{
_employee = employee;
}
public string Name { get { return _employee.Name; } }
public string CompanyName { get { return _employee.Company == null ? "Unemployed" : _employee.Company.CompanyName; } }
}
Then, given an IEnumerable<Employee> you can project your employee data into this view model and set it as the ItemsSource of your DataGrid - e.g.
IEnumerable<Employee> employees = GetEmployeesFromDatabase();
DataGrid1.ItemsSource = employees.Select(x => new EmployeeViewModel(x));
You would normally set the ItemsSource via a xaml binding here rather than setting it directly in code but that would involve the use of a parent ViewModel set as the DataContext of the View and I'm trying to keep things simple.
Another way to accomplish this with a DataGrid would be to forgo the use of a View Model, bind directly to an IEnumerable<Employee> collection and set the column bindings explicitly - e.g.
<DataGrid ItemsSource="{Binding Employees}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Employee Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Company Name" Binding="{Binding Company.Name}" />
</DataGrid.Columns>
</DataGrid>
Note that with the second example, you won't get "Unemployed" appearing in the Company Name column where there is no associated company for an employee.
EDIT: To clarify the point about setting the items source for your Grid from a property on a 'main' view model bound to the View, you might create a ViewModel class that represents the whole of the current view. e.g.
public class MainViewModel
{
private readonly IEnumerable<Employee> _employees;
public MainViewModel(IEnumerable<Employee> employees)
{
_employees = employees;
}
public IEnumerable<Employee> Employees
{
get { return _employees; }
}
}
You'd set this as the DataContext for the Window / UserControl.
e.g. in simple cases you could do this in the constructor for your Window (although calls to the database should really be asynchronous for WPF apps where possible).
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel(GetAllEmployees());
}
Then you can set up the binding on your Grid like this:
<DataGrid ItemsSource="{Binding Employees}" ...
The data context for the main window is then used for all the child controls contained therein except where you explicitly set the DataContext on a child control. This technique becomes useful where you have more complex ViewModels with many properties and commands. If you are interested in finding out more you should take a look at the MVVM pattern and either or both of the Prism / Caliburn frameworks.

Related

Fill a datagrid with EF6 and MVVM

I think I pretty much have had every page there is about this subject, but I can't seem to figure out how to properly display data in a WPF datagrid. I'm still learning the ropes, but I believe everything should work, yet it doesn't. I already have a SQL Server database up and running with actual data in it, the datagrid still shows up empty however. Is there something I overlooked?
This is datagridview.xaml
<DataGrid x:Name="DatagridC" AutoGenerateColumns="False" ItemsSource="{Binding Schools}" >
<DataGrid.Columns>
<DataGridTextColumn Header="SchoolID" Binding="{Binding SchoolID}" Width="SizeToHeader" />
<DataGridTextColumn Header="School" Binding="{Binding Name}" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
And this is datagridview.xaml.cs (Based on an earlier question about this topic) (I know this would be smart to do with INotifyPropertyChanged, still schooling myself on what that is, but my main goal right now is to show the data)
private ObservableCollection<School> schools;
public ObservableCollection<School> Schools
{
get { return schools; }
set
{
schools = value;
}
}
And the school class
public class School
{
public int SchoolID { get; set; }
public string Name { get; set; }
}
}
I've tried multiple things already, sometimes I get a warning popup that no datacontext has been found, but I'm not understanding in what way that is neccesary and this time.
I'm betting I'm making rookie mistakes, but could someone point me to a right direction?
I think as per your warning your database context is not available. you can troubleshoot this in the following steps.
Check your DB connectivity
Check the context if it is able to open the database
Check the query generated by your EF
execute the query got from the above steps in SSMS
if all these steps are passing then check your data adapter
if the data adapter is ok then the data grid should populate
if still there is any issue update your code for DB context and data adapter so we can look where it is failing
You need to set the DataContext of the DataGrid or any of its parent elements. You could for example set the DataContext in the constructor of datagridview.xaml.cs:
public datagridview()
{
InitializeComponent();
DataContext = this;
}
The other option is to specify the source of the binding in the XAML markup:
<DataGrid x:Name="DatagridC" AutoGenerateColumns="False"
ItemsSource="{Binding Schools, RelativeSource={RelativeSource AncestorType=Window}}">
The above markup snippet assumes that the class where the Schools property is defined is the parent window of the DataGrid, i.e. that datagridview.xaml.cs is a Window. If it's a UserControl, you should change the AncestorType to UserControl.

How to Bind entire datagrid to view MVVM following the MVVM pattern

I want to dynamically display data in form a of a table. The control I chose for different reasons is a DataGrid. That solution worked to a certain point, but when I needed to dyanmically alter which properties I wanted to display in the datagrid I encountered problems. When I bind its ItemsSource directly to an observable it shows all properties in the specific object, which I dont want. I want to Create a datagrid programatically and push it into the view somehow. Is that even possible?
I've been looking now for a long time but I can't seem to find a good solution. It's also important that the solution follows the MVVM-pattern.
public class MyViewModel
{
public Datagrid dataGrid = null;
public MyObject gridObject = null;
public MyViewModel ()
{
gridObject = new MyObject();
dataGrid = CreateDataGrid(gridObject);
}
private Datagrid CreateDataGrid(object objectName) {}
}
After this I want to bind the grid to the XAML. Is it possible or is there a better way of doing this, rather then a DataGrid? I want the functionality built into the datagrid like moving columns, mark specific rows etc..
You shouldn't create a DataGrid in a view model. This is no MVVM. You create it in the view.
If you don't want the DataGrid to autogenerate the columns for you, you should set its AutoGenerateColumns property to false.
You could then define the columns you want yourself, either in the XAML markup:
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="..." Binding="{Binding Property}" />
</DataGrid.Columns>
</DataGrid>
Or programmatically:
dataGrid.Columns.Add(new DataGridTextColumn() { Header = "header...", Binding = new Binding("Property") });
The view model should return an IEnumerable<T> from a public property that the DataGrid in the view binds to:
<DataGrid ItemsSource="{Binding ViewModelCollection}" ...
Make sure that you set the DataContext of the view to an instance of the view model for the binding to work:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}

Should I display data using MVP and WPF through binding?

I'm doing a paper on code-design and I'm comparing MV-C,P and VM to see which one is the best fit for WPF. During my research I realised that MVVM is the obvious choice cause of the databinding among other criterias.
Even If I know this I have to "prove" this in a sense so I'm creating an application both in MVP and MVVM that does exactly the same thing but handles code differently. With this I will explain the pros and cons with these code-patterns but in my creating with the MVP-application I reached a problem.
I have a model with the "buisness-logic" and my presenter creates a list of these model objects that my view can display.
My problem is how I display them
In MVVM I bind my list to a ListBox cause that is how MVVM is "made" to do.
EG
<Window.Resources>
<DataTemplate DataType="{x:type model:Mymodel}">
//Some DataTemplate Definition
</DataTemplate>
</Window.Resources>
Then binding to my Listbox
<ListBox ItemSources={Binding someProperty} />
It's not fully coded but you get the gesture
But if I've understood correctly, binding with MVP is not how it should be.
You should not bind anything in MVP cause that is not how it is supposed to work, or am I wrong here?
So if I shouldn't bind data, how can I display this list of my model objects in a ListBox so it doesn't say
Model Object
Model Object
Model Object
Model Object
I understand that you should use MVVM for WPF but for the sake of proving why it's better I need to show how MVP can work in WPF also.
When you're using WPF, it is as you said made to work with MVVM, trough the data-binding. MVP was often used with Windows-form where no data-binding was available. If you want your applications to have the same functionality and use the same technology (WPF) you can't avoid using binding or it's at least more difficult to do. As long as you talk through the presenter to your model you're still using MVP. You can decide for yourself if you want to use
Passive View - The presenter handles ALL dialog between the view and model
SuperVising Presenter - The View knows about the model and the presenter handles "difficult-code" that is to much to be handled between the view and model.
If you're using binding I would say (Unsure about it) you're using SuperVising Presenter which is not "recommended", but using MVP in WPF is not recommended either so...
EDIT Example
For instance if you want to display a list you need to have an interface that has a property of a list containing objects you want to display.
public interface myinterface
{
ObservableCollection<YourModel> ListName {get; set;}
}
and then in your presenter just "push" the data to that list
private myinterface _my;
public Presenter(myinterface my)
{ this._my = my;}
_my.ListName = // Add whatever Data you want into this list.
And in your view
<ListBox ItemSource ={Binding ListName}>
<ListBox.ItemTemplate>Set how you want to display the list</ListBox.ItemTemplate>
This is a unclear example but hopefully can give you the idea how MVP works with WPF ( in a small way)
I cannot add as much code as I want to the comment so I'll post an answer. If something is unclear just give me a feedback so I'll give you more details.
From the example you've shown I would go about and make binding from View to Presenter which should be the bridge between View and Model as you can see in here :
( Image from wikipedia article )
View should send events/changes to the presenter and presenter should be the "brain/logic" of the View which decides if it should update the Model or not.
Assuming you've the View like this one :
<UserControl x:Class="EntryNamespace.MeView"
... >
<!-- ListItems should return collection of Presenters -->
<ListView ItemsSource="{Binding ListItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- Elementcontent should be a property inside Presenter that returns value from Model -->
<Button Content="{Binding ElementContent}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
You can create a Presenter like this one :
class Presenter : INotifyPropertyChanged
{
public ObservableCollection<ListItemPresenter> ListItems
{
get { return GetItems(); }
set { SetItems(value); }
}
ObservableCollection<ListItemPresenter> GetItems()
{
// private logic to retrieve `ListItemPresenter` collection from model
var collection = new ObservableCollection<ListItemPresenter>();
foreach(ListItem listItem in Model.Items)
collection.Add(listItem.GetPresenter());
return collection;
}
void SetItems(ObservableCollection<ListItemPresenter> objects)
{
// private logic to transfer `ListItemPresenter` collection to model
// remember to call NotifyPropertyChanged("ListItems");
Model.Items.Clear();
foreach(ListItemPresenter presenter in objects)
Model.Items.Add(presenter.GetModel());
NotifyPropertyChanged("ListItems");
}
}
You Model can look like this:
public class ListItem
{
ListItemPresenter m_Presenter;
public ListItemPresenter GetPresenter()
{
return m_Presenter;
}
string m_ElementContent;
public string ElementContent
{
get { return m_ElementContent; }
set { m_ElementContent = value; }
}
public ListItem(ListItemPresenter presenter)
{
m_Presenter = presenter;
}
}
Another way to use Presenter can look like such :
Assuming you've similar View just create a Presenter:
public class Presenter
{
ObservableCollection<ListItem> m_ListItems = new ObservableCollection<ListItem>();
public ObservableCollection<List> ListItems
{
get { return m_ListItems; }
}
public Presenter(MeView view)
{
Binding binding = new Binding("ListItems");
binding.Source = ListItems;
view.MeListView.SetBinding(ListView.ItemsSourceProperty, binding);
// other view events, bindings etc.
}
}
It's not the best way of communicating between View and Model indirectly, but should give you small hint. It will be up to you if you want to split your controls that each will have it's own Presenter or if you want one per window.

Deleting child item from Linq2Sql bound TreeView

I'm having a TreeView control in WPF which displays a list of tasks on the first level. Each task has a list of persons. Both the tasks and persons are stored in a database. I wrote 2 viewmodel classes which encapsulate the Linq2Sql classes. The TreeView consists of 2 hierarchical DataTemplates which refers to the viewmodel classes. Displaying the data works well and I can add task and persons without any problems.
But now I have the problem that I want to delete a person underneath a task from a contextmenu. My problem is that I am not able to access the parent task and therefore cannot update the persons collection. I know which person to delete but not to which task it belongs.
What is the best way to achieve this?
Thanks!
Gerrit
using System;
class ViewmodelPerson
{
public ViewmodelPerson(LinqPerson P)
{
DBPerson = P;
}
LinqPerson DBPerson;
}
public class ViewmodelTask
{
public ViewmodelTask(LinqTask DBTask)
{
this.DBTask = DBTask;
_Persons = from P in DBTask.Person
select new ViewModelPerson(P);
}
LinqTask DBTask;
List<ViewmodelPerson> _Persons;
List<ViewmodelPerson> Persons
{
get
{
return _Persons;
}
}
public void AddPerson(ViewmodelPerson P)
{
}
}
class BaseViewModel
{
public List<ViewmodelTask> Tasks
{
get
{
// Code to get the tasks from Database via Linq
}
}
}
SOLUTION
Because I was not able to get the parent task to which the person belongs I simply added a member ParentTask to my person class. This member needs to be passed within the constructor. When the DeletePerson method is called on the ViewmodelPerson class the Object is deleted in the database and I have access to the parent Task object and can clean up the List as well. Afterwards ChangedProperty("Persons") of IPropertyChanged is called and the WPF tidies up the UI just like magic!
I was just wondering if this approach has a big impact on memory consumption if there are a lot of persons and tasks.
If I understand you correctly, you have a ContextMenu, but you can't access the DataContext of your view from its scope. This is a common problem in WPF and the solution is to 'put the DataContext' into a Tag property that we can later retrieve from the ContextMenu:
<DataTemplate DataType="{x:Type YourNamespace:YourDataType}">
<Border Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType={
x:Type YourViewNamespace:NameOfThisControl}}}" ContextMenu="{StaticResource
YourContextMenu}">
...
</Border>
</DataTemplate>
Then you need to set this Tag property to the DataContext of the ContextMenu using a handy property named PlacementTarget:
<ContextMenu x:Key="YourContextMenu" DataContext="{Binding PlacementTarget.Tag,
RelativeSource={RelativeSource Self}}">
<MenuItem Header="First Item" Command="{Binding CommandFromYourViewModel}" />
</ContextMenu>
To find out more, please take a look at the Context Menus in WPF post on WPF Tutorial.NET.

Are DataContext a kind of new version of DataSource?

What is a DataContext in C#? Are DataContext a kind of new version of DataSource?
(It sounds you mean the FrameworkElement.DataContext property that appears in WPF. If not, oops.)
It's somewhat similar to DataSource, but is much more flexible. In WPF, a DataContext can be literally any object that has properties. To bind to a property, you just specify its name and WPF takes care of the rest for you by fishing the specified property out of the DataContext with some reflection magic. (Fields are not supported.)
For example, if you're binding a view (say, a UserControl or Window) to an Employee object:
class Employee {
public string FirstName { get; set; }
public string LastName { get; set; }
// more stuff. . .
}
then you just set its DataContext to that object. Then the XAML for displaying information from that Employee object could be as simple as:
<Label Content="{Binding FirstName}"/>
<Label Content="{Binding LastName}"/>
Optionally, the DataContext can provide additional functionality that allows for richer binding. For example:
If it implements INotifyPropertyChanged or has dependency properties, changes to the DataContext will be automatically reflected in the UI.
Properties that have setters support two-way binding.
If it implements INotifyDataErrorInfo then you can do form validation.
If it's an ADO.NET object, you get all the usual ADO.NET binding magic.
A DataContext represents the database in either LINQ to SQL or Entity Framework.
It's not quite an analog to anything else in .NET as it handles a lot of different things (change tracking, sql generation, etc).

Categories