How to bind Selected item of Combobox to two different Property? - mvvm

I am developing project using MVVM pattern.In the project I have two viewmodel namely
CountryViewModel and 2. EmpViewModel
In countryviewmodel I have stored information about country,state,city etc.
In EmpViewModel I have a control which have combo box which displays country name and selected value is set to country id which are in CountryViewModel.
Here is code:
<ComboBox Grid.Row="0" Grid.Column="1" Margin="3"
ItemsSource="{Binding CountryViewModel.Countries}" SelectedValue="{Binding Title}"
SelectedItem="{Binding CountryViewModel.SelectedCountry,Mode=TwoWay}"
SelectedValuePath="Country_Id" DisplayMemberPath="Title">
</ComboBox>
This is working fine.
I have local property country id in EmpViewModel and want to bind it to SelectedValue property of Combobox which I can get if I remove CountryViewModel from CountryViewModel.SelectedCountry.
But problem is I have another combobox for state which is dependent on Country combo box.
Edit: i.e in Country ViewModel I have called method GetAllState() when SelectedCountry changes.
So can I bind SelectedValue property of Combobox to both CountryViewModel.SelectedCountry from CountryViewModel and Country_Id from EmpViewModel?

I found a workaround
I have write following method in an interface:
public interface IViewModel
{
T GetValue<T>(string propertyName);
}
In country view model I implement this method as:
public override T GetValue<T>(string propertyName)
{
T result = default(T);
result = (T)Convert.ChangeType(this.SelectedCountry, typeof(T), null); }
And in Emp View Model I added following line:
newEmp.Country_Id = this.CountryViewModel.GetValue<Country>("SelectedCountry").Country_Id;

Related

How to show all inherited interfaces in GridControl

We have a GridControl and we are assigning the ItemsSource to a collection of interface items. The interface used in the collection inherits from another interface, and the problem we are running into is that only items directly defined in the top level interface are being shown in the GridControl
Below is a very simplified example of the behavior that we are seeing.
xaml code defining the GridControl
<Window x:Class="WpfThrowaway.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfThrowaway"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<dxg:GridControl x:Name="DataGrid" Background="#363636" Foreground="#FFFFFF" EnableSmartColumnsGeneration="True" AutoGenerateColumns="AddNew">
<dxg:GridControl.View >
<dxg:TableView x:Name="TableView" AllowEditing="False" />
</dxg:GridControl.View>
</dxg:GridControl>
</Grid>
</Window>
Concrete item implementation
class ConcreteItem : ILevel1
{
public string Level1String => "Level 1 string";
public double Level2Double => 2;
}
Level 1 interface (type used in the ItemsSource collection)
interface ILevel1 : ILevel2
{
string Level1String { get; }
}
Level 2 interface
interface ILevel2
{
double Level2Double { get; }
}
Code behind to initialize ItemsSource in the MainWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var concreteItemCollection = new List<ILevel1>();
for(var i = 0; i < 100; i++)
{
concreteItemCollection.Add(new ConcreteItem());
}
DataGrid.ItemsSource = concreteItemCollection;
}
}
Resulting data grid
What we want and would expect is for the GridControl to show two columns Level1String and Level2Double, but only the item explicitly defined in the ILevel1 interface is showing up in the grid.
Is there any workaround for this? How do we get all properties from inherited interfaces to show up as well?
A bit of a hack that works is to cast the top level interface to an object. It will trick the grid control into auto generating the columns based on the concrete implementation, which will get you all of your properties.
DataGrid.ItemsSource = concreteItemCollection.Select(x => (object)x);

ZK: loading value/text into selectbox

I have a selectbox and want to load the value and text into the template, similar to an HTML dropdown box. I am using ZK framework with Java on the back end.
<selectbox id="buListbox" model="${$composer.buModel}" >
<template name="model">
<label value="${each}" />
</template>
</selectbox>
When using ZK, you don't need the value to identify the selected object like in HTML.
When using the MVC pattern, binding a model via model attribute, the selected item is also stored in that model and can be retrieved in java via model.getSelection().
Furthermore, a model is not restricted to lists of String, but it can hold any object type you want. In the template section, you can display any property of that object. Then the properties' toString() method is used to get the value which is displayed. This also applies to ${each} itself.
Example:
Assuming your model is a ListModelList of type ValueType:
public class ValueType {
private String value;
private String text;
public ValueType(String value, String text) {
this.value=value;
this.text=text;
}
public String getText() {
return this.text;
}
public String getValue() {
return this.value;
}
}
private ListModelList<ValueType> typesModel;
public ListModelList<ValueType> getTypesModel() {
return typesModel;
}
You than can use the selectbox's model/template to display it's text property:
<selectbox id="typesSelectbox" model="${$composer.typesModel}">
<template name="model">
${each.text}
</template>
</selectbox>
In java, you then get the selected item via typeModel.getSelection() .
Here you can find a working ZKFiddle example.

Project structure for EF/Silverlight application

I am currently working on finding a good project structure for a 3-tier solution to avoid unnecessary work in a new project.
The project will consist of a core product which has
model project which holds the EF code first models
project which has the business logic and communication logic on the server
repository project on the client
silverlight project with views and view models (would like to use caliburn.micro here)
The problem is now that a customer could have some special requirements which could lead to changes in all the above projects. So my thought was that I could just use the base structure and create the same structure for a customer. If there are no changes I would just have empty classes that just extend the base class and add nothing.
Which brings me to the following problems:
Is it a problem in Entity Framework (code first) to have base classes in one project (which is already fully functional) and have another project which may extend the model classes with new field?
Is it a problem in XAML to change a user control? For example if I have a user control that consists of five textboxes in my core and I want to change the second box to a radio button, but nothing else.
I will also accept changes to the project structure, if there is a better way to handle customer specific changes.
Edit: I will probably use the following approach to solve the problem.
Entity Framework:
With code first it seems to be possible to have one model project extend another project. This means I could write something like:
public class CoreAddress{
[Key]
public int AdrId{get; set;}
public string Street {get;set;}
}
public class CustomerAddress : CoreAddress{
public string StreetNumber {get; set;}
}
Only thing that is needed in order to make that work is a line inside the DbContext:
(this as IObjectContextAdapter).ObjectContext.MetadataWorkspace.LoadFromAssembly(typeof(<entity from other assembly>).Assembly);
XAML
To get a similar behavior in XAML I had to use Caliburn.Micro (in combination with MEF) which is a great help here.
I would create UserControls that include ContentControl elements which are dynamically fetched by using MEF. That means I have again a core project with all the views and ViewModels. If I need to exchange a special control somewhere for a customer I change the control to a ContentControl and create a core view and ViewModel for it (which is the same as it was before the change request).
This ViewModel for the ContentControl is annoted with an export interface and ExportMetadata to set a priority of 1.
Now I create another project with another UserControl that has some other control instead of the core control and annotate it again as an export with the same interface but set my priority higher and so the customer specific control is loaded.
Short example for this:
Main user control and view model:
<UserControl x:Class="SilverlightApplication5.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<ContentControl x:Name="Item"/>
<TextBox x:Name="TextItem" Text="asdf"/>
</StackPanel>
</Grid>
</UserControl>
public class TestViewModel : Screen
{
private object viewModel;
private Lazy<IMyViewModel, IPluginMetadata>[] _orderEditorFactory;
[ImportMany(typeof(IMyViewModel), AllowRecomposition = true)]
public Lazy<IMyViewModel, IPluginMetadata>[] OrderEditorFactory
{
get { return _orderEditorFactory; }
set
{
_orderEditorFactory = value;
Item = _orderEditorFactory.OrderByDescending(lazy => lazy.Metadata.Priority).First().Value;
}
}
private object _item;
public object Item
{
get { return _item; }
set
{
_item = value;
NotifyOfPropertyChange(() => Item);
}
}
}
Core Control:
<UserControl x:Class="SilverlightClassLibrary2.MainControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<StackPanel>
<TextBlock x:Name="Test" Text="Text from Core control"/>
</StackPanel>
</UserControl>
[Export(typeof (IMyViewModel))]
[ExportMetadata("Name", "Pluginc")]
[ExportMetadata("Priority", 30)]
public class MainControlViewModel : Screen, IHarnessAware, IMyViewModel
{
}
Customer specific control:
<UserControl x:Class="SilverlightClassLibrary1.CustomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<RadioButton x:Name="Test" Content="{Binding Path=Test}"/>
</Grid>
</UserControl>
[Export(typeof(IMyViewModel))]
[ExportMetadata("Name", "Plugind")]
[ExportMetadata("Priority", 2)]
public class CustomViewModel : MainControlViewModel, IHarnessAware, IMyViewModel
{
}
Export interface:
public interface IMyViewModel
{
}
ExportMetadata interface:
public interface
IPluginMetadata
{
string Name { get; }
[DefaultValue(0)]
int Priority { get; }
}
I did use this to answer the question, because I am still interested in input from other people who may have already solved a simlilar problem.
About project structure:
You can create a base project, with every concern layer you need. Model, business, views, repositories and so on.
Also create some basic flow, for example, a single view with its controller up to the repository. Save it in your codebase, then fork it whenever you need to create a new project.
Now instead of using time to set up, you'll just need some time to customize as your project require.
About XAML: IMHO, if you change the component, you should be sure it returns the same datatype as your control expects. If you swap a textbox for a checkbox, be sure the check returns a string to the controller.

Entity Framework 4 - Navigation Property Object Null on Client Side

There are two tables of interest in my entity conceptual model: tblProducts and tblInstalledProducts.
Each installed product has a ProductID foreign key linking it to a specific product, which was set up automatically as a navigation property.
Within the entity domain service I have the following query:
public IQueryable<tblInstalledProduct> GetInstalledProductsBySiteID(string SiteID)
{
ObjectSet<tblInstalledProduct> installedProducts = this.ObjectContext.tblInstalledProducts;
var filterBySite =
from p in installedProducts.Include("tblProduct")
where p.SiteID == SiteID
select p;
return filterBySite;
}
I have a DataGridView bound to a DomainDataSource configured to use this query.
When I debug this query, p.tblProduct and p.tblProductReference are populated as expected. The problem arises when trying to access the tblProduct property of any tblInstalledProduct from the client side.
//Find associated install record for the selected product
tblInstalledProduct selectedInstall =
Context.tblInstalledProducts.Where(
p => p.SiteID == "Site1" && p.ProductID == 38
).First();
string productName = selectedInstall.tblProduct.ProductName;
For some reason tblProduct is always null. I've tried .Include() / .Load() and can't seem to get it to populate itself.
Why is tblInstalledProduct.tblProduct loaded up as expected on the service side of things, but is seemingly inaccessible on the client side?
Thanks for reading.
Edit:
XAML DataSource:
<telerik:RadDomainDataSource x:Key="InstalledProductsDataSource"
Name="InstalledProductsDataSource"
DomainContext="{StaticResource DomainContext}"
AutoLoad="True"
QueryName="GetInstalledProductsInfoBySiteID"
SubmittedChanges="InstalledProductsDataSource_SubmittedChanges">
<telerik:RadDomainDataSource.QueryParameters>
<telerik:QueryParameter
ParameterName="SiteID"
Value="{Binding SelectedValue,ElementName=SiteList}" />
</telerik:RadDomainDataSource.QueryParameters>
</telerik:RadDomainDataSource>
XAML DataGrid:
<telerik:RadGridView x:Name="InstalledProductsGridView"
ItemsSource="{Binding DataView, Source={StaticResource InstalledProductsDataSource}}">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Product Name" DataMemberBinding="{Binding ProductName, Mode=TwoWay}" />
<telerik:GridViewDataColumn Header="Version" DataMemberBinding="{Binding ProductVersion, Mode=TwoWay}" />
<telerik:GridViewDataColumn Header="Description" DataMemberBinding="{Binding Description, Mode=TwoWay}" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
Right now the grid is bound to a collection of tblProducts, but I'd like to bind it to a collection of tblInstalledProducts (as there is some extra information in that table that I need access to) like so:
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="DateInstalled" DataMemberBinding="{Binding DateInstalled, Mode=TwoWay}" />
<telerik:GridViewDataColumn Header="Product Name" DataMemberBinding="{Binding tblProduct.ProductName, Mode=TwoWay}" />
<telerik:GridViewDataColumn Header="Version" DataMemberBinding="{Binding tblProduct.ProductVersion, Mode=TwoWay}" />
<telerik:GridViewDataColumn Header="Description" DataMemberBinding="{Binding tblProduct.Description, Mode=TwoWay}" />
</telerik:RadGridView.Columns>
you need to do something like this
tblInstalledProduct selectedInstall = Context.GetInstalledProductsBySiteID("Site1").Where(p=> p.ProductID == 38 ).FirstOrDefault();
string productName="";
if(selectedInstall !=null)
{
productName= selectedInstall.tblProduct.ProductName;
}
for testing try to use;
public IQueryable<tblInstalledProduct> GetInstalledProductsNew()
{
//Im nut Sure of 'tblProduct' or 'tblProducts' it is dependent on your relations
return this.ObjectContext.tblInstalledProducts.Include("tblProduct");
}
For anyone else having problems with this, I did eventually find the solution. You need to use both .Include() on the query to tell it to load related objects, as well as the [Include] attribute in the metadata to allow those related objects to be serialized and sent to the client.

Getting Readonly Databind using EF POCO Objects

I am using EF4 with WPF. I am databinding to the DataGrid in a Master-Detail style. Think of the Northwind Customers -> Orders -> OrderDetails.
What I am finding is that when I use POCO objects, the Orders and OrderDetails grids are read-only. If I revert to using the designer generated entities they become editable.
The binding XAML looks like this:
<Window.Resources>
<CollectionViewSource x:Key="CustomersViewSource" d:DesignSource="{d:DesignInstance my:Customer, CreateList=True}" />
<CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=Orders, Source={StaticResource CustomersViewSource}}" />
</Window.Resources>
<Grid DataContext="{StaticResource CustomersViewSource}">
<DataGrid ItemsSource="{Binding}" >
<DataGrid ItemsSource="{Binding Source={StaticResource CustomersOrdersViewSource}}" >
(I've removed attributes not relevant to databinding, of course.)
Then there's the standard form load event to bind the context instance:
Dim NorthwindEntities As BindTest.NorthwindEntities = New BindTest.NorthwindEntities()
Dim CustomersViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("CustomersViewSource"), System.Windows.Data.CollectionViewSource)
CustomersViewSource.Source = NorthwindEntities.Customers
The grids populate, but the second is readonly if I'm using my POCO objects, editable if they are the standard EF generated objects.
The key seems to be in the navigation properties of the entities. My POCO objects use:
Public Overridable Property Orders() As ICollection(Of Order)
Get
If _Orders Is Nothing Then _Orders = New HashSet(Of Order)
Return _Orders
End Get
Set(ByVal value As ICollection(Of Order))
_Orders = value
End Set
End Property
Whereas the EF objects are much more complicated:
<XmlIgnoreAttribute()>
<SoapIgnoreAttribute()>
<DataMemberAttribute()>
<EdmRelationshipNavigationPropertyAttribute("NorthwindModel", "FK_Order_Details_Orders", "Orders")>
Public Property Order() As Order
Get
Return CType(Me, IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order)("NorthwindModel.FK_Order_Details_Orders", "Orders").Value
End Get
Set
CType(Me, IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order)("NorthwindModel.FK_Order_Details_Orders", "Orders").Value = value
End Set
End Property
For the lack of some better wording, there seems to be some magic in either the attributes for the EntityCollection type. ICollection isn't a readonly interface and a HashSet isn't readonly either.
Any ideas about how to get POCO to work here or am I stuck with EF derived objects? (Makes unit testing difficult.)
Thanks.
The problem is likely that the Orders and OrderDetails collections are of type ICollection<T> / HashSet<T> in your POCO example. The WPF datagrid internally does not work with the collection directly but rather with an associated "collection view". When you bind the collection to the DataGrid the WPF binding engine creates this internal collection view based on the type of the collection.
If your collection implements only IEnumerable or only ICollection the type of the created collection view is CollectionView, a class which does not implement IEditableCollectionView. That's the reason why you can't edit the DataGrid when you bind a HashSet to it.
The DataGrid needs a collection view which implements IEditableCollectionView to allow editing. This is for example the ListCollectionView (which also derives from CollectionView). WPF creates this type of collection view if your source collection implements the IList interface.
So, to fix the problem you should change the type of the Orders property of your POCO to IList:
Public Overridable Property Orders() As IList(Of Order)
Get
If _Orders Is Nothing Then _Orders = New List(Of Order)
Return _Orders
End Get
Set(ByVal value As IList(Of Order))
_Orders = value
End Set
End Property
Edit
According to #Allon Guralnek's comment below it is necessary to implement the non-generic IList interface to get an editable DataGrid. This is the case for List(Of T), therefore the code above will still work. Other implementations which only implement the generic IList(Of T) but not the non-generic IList won't make the DataGrid editable.