Autogenerating columns from empty results in Silverlight - silverlight

I'm doing some rapid prototyping and is trying to mock out an admin interface for a website and went with WCF RIA Services. I'm able to expose and consume the domain services from server to client, but I'm struggling with getting columns autogenerated in the datagrid when the result of the query on the server holds no data.
<riaControls:DomainDataSource Name="domainDataSource1"
LoadSize="20" QueryName="GetUsers" AutoLoad="True" >
<riaControls:DomainDataSource.DomainContext>
<ds:CobraDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<sdk:DataGrid ItemsSource="{Binding Data, ElementName=domainDataSource1}" AutoGenerateColumns="True" IsReadOnly="False" Width="250" Height="150" >
</sdk:DataGrid>
This renders an empty 250x150 datagrid (no columns/no rows). I was expecting that the colunms for the user entity were displayed even though no data was returned from the server since the view would kind of suck otherwise initially. My brain can't can't seem to figure out what is wrong, so I'll crowdsource with stackoverflow.
UPDATE: I was kind of expecting the result from the query to be a typed enumeration, but it appears that the result of the query on the DomainDataService is just IEnumerable but not typed, so the internal logic needs to look into the list to discover what kind of data it is containing.
So the updated question is: Can I give the DataGrid a hint on what type of data will be returned or otherwise autogenerate columns in the grid (through XAML or code)??

I guess your ItemsSource enumeration is not typed, right? If it's just a list of objects, the datagrid will not find the entity public properties.
I don't know how your entity class is, but try this code to see what I'm talking about:
Somewhere:
public class User {
public string Name { get; set; }
public int Age { get; set; }
}
Xaml:
<my:DataGrid x:Name="datagrid"/>
Codebehind:
public MainPage() {
InitializeComponent();
datagrid.ItemsSource = new List<User>();
}
The list is empty but datagrid will pick the column names since I'm using a List of Users. It could be an Enumeration of Users, etc..
I hope it helps,
Cheers
EDITED: about the updated question: You could try using a Converter.

Related

Best model for WPF+MVVM+EF 6.1

I’m trying to figure out best model for my WPF+MVVM+EF 6.1 application and I’m little confuse how to do this right after watching many EF courses and blogs. When modeling application in WPF there is a need for INotifyPropertyChanged, Observablecollection and also adding some additional calculated properties (not persisted on the database). I’m considering this solutions:
Change EF T4 template and implement INotifyPropertyChanged, change collections to Observablecollection and add additional fields in partial class. This brings to bind to EF model but looks simple and easer to maintain;
Use separate class for domain objects and rewrite data between them – for instance using automapper. This have separation of concerns but all updated and inserted entities have to be translated to appropriate EF entities.
Implement new class that have EF class nested inside new class with all properties wrapped and change tracked – this in other hand brings redundant code.
What will be best solution for this which not involve writing redundant code?
How about using the generated EF entities as it is and creating a DTO model that implements INotifyPropertyChanged only when it is necessary.
Not in every case you need to be able to synchronize the data between the view and the model right away.
Don't abuse the usage of INotifyPropertyChanged. It will be too late when you realize that you have a lot of extra code that doesn't actually need two way binding, a lot of repetitive work and can't be reused by other client view (could be an asp.net) because it's too technology specific (for example dependency property).
And the MVVM pattern works fine for a simple data display without you have to implement INotifyPropertyChanged for the binding data.
public class Order
{
public string OrderNo { get; set; }
public DateTime Date { get; set; }
}
public class WindowViewModel
{
public WindowViewModel()
{
var orders = Service<TheEntity>.Get();
Array.ForEach(orders, order => Orders.Add(order));
}
private readonly ObservableCollection<Order> _orders = new ObservableCollection<Order>();
public ObservableCollection<Order> Orders
{
get { return _orders; }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new WindowViewModel();
}
}
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}">
<ListView ItemsSource="{Binding Orders}">
<ListView.View>
<GridView>
<GridViewColumn Header="OrderNo" DisplayMemberBinding="{Binding OrderNo}"/>
<GridViewColumn Header="Date" DisplayMemberBinding="{Binding Date}"/>
</GridView>
</ListView.View>
</ListView>
</Window>
The best way is not to disturb auto-generated code from EF, even if you wrap it inside, new class, you still need some mechanism to track and save changes, doing it so completely overrides for what entity framework is designed for (less coding in DAL). The best way is to have properties (observable collection or i notify properties in VM- View Model). You might need to put little more code into your application but that helps you and to maintain the code and it will be total decoupled from other modules (BL, DAL, ..) of the application.

Where can I use a DataTemplate in something that isn't a repeater type of control?

Ok, I know it's kind of weird, but I am trying to create a data driven panel that reconfigures itself based on a DataTemplate member that I have on a object. Right now I am accomplishing this by using an ItemsControl bound against a dummy list of 1 bogus item so that I get a single instance of the data template. It just seems silly to have to do this in an ItemsControl, but I can't think of anything that will use my DataTemplate without trying to do it against a list of items. Anyone have any idea?
Just for clarity, let's say I have a Widget class:
public class Widget
{
public string Name { get; set; }
public DataTemplate MyTemplate { get; set; }
public List<object> DummyList = new List<object> { new object(); }
}
and the Xaml something like:
<ItemsControl ItemsSource={Binding DummyList} ItemTemplate={Binding MyTemplate}/>
I can then create a collection of Widgets and populate each one with the correct data template based on the object's status.
Anyway, as I said, this works... I'd just like to find a more elegant solution than using an ItemsControl if anyone knows of one.
Chances are that you could also just set ContentTemplate="{Binding template}" if your control (that you wish to dynamically modify its contents - e.g. Button inside etc.) is ContentControl. I found that often 'overlooked' as it's not immediately visible or intuitive, but saves you adding extra 'content'.
Or you can use ContentControl - or presenter as suggested already.
<ContentPresenter ContentTemplate="{Binding MyTemplate}"/>

Why does one of MY WPF DataGrids give the "'EditItem' is not allowed for this view" exception?

I have read all the Q&A I could find here and on the MS forums about this exception, and tried most of the suggestions that I understood, and a few others. It seems that this exception can come up for a wide range of causes.
As with others, I have a WPF DataGrid bound to a collection, which throws this exception when one tries to edit one of the cells. They are set to be write-able, the collection is an ObservableCollection, I've implemented get and set handlers which send notification messages.
The suggestions I haven't tried are the ones involving implementing IList's non-generic interface, because I have no idea what I would do to do that. Also, I have many DataGrids bound to various lists and collections in my app which work, and this one used to work when it was bound to a LINQ collection.
Please help me figure out what I need to do here.
The Data Grid is:
<DataGrid Name="dgIngredients" Margin="567,32,0,44" Width="360" ItemsSource="{Binding}" IsReadOnly="False"
AutoGenerateColumns="False" HorizontalAlignment="Left" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Columns>
<DataGridTextColumn Width="63" Header="Percent" Binding="{Binding Preference}" IsReadOnly="False" />
<DataGridTextColumn SortDirection="Descending" Width="301" Header="Ingredient" Binding="{Binding Ingredient}" IsReadOnly="True" CanUserSort="True" CanUserReorder="False" />
</DataGrid.Columns>
</DataGrid>
The column being edited is the non-read-only one, Preference.
The collection is:
private ObservableCollection<RAM_Ingredient> MemberIngredientPrefs = new ObservableCollection<RAM_Ingredient>();
The binding is:
dgIngredients.DataContext = MemberIngredientPrefs.OrderBy("Ingredient",true);
RAM_Ingredient is:
public class RAM_Ingredient : INotifyPropertyChanged
etc.
Where RAM_Ingredient.Preference is:
private int _Preference;
public int Preference
{
get
{
return _Preference;
}
set
{
// This is needed to send notification of changes (and to not throw an exception on grid edit!):
if ((_Preference != value))
{
SendPropertyChanging();
_Preference = value;
SendPropertyChanged("Preference");
}
}
}
The exception is:
System.InvalidOperationException was unhandled
Message='EditItem' is not allowed for this view.
Source=PresentationFramework
StackTrace:
at System.Windows.Controls.ItemCollection.System.ComponentModel.IEditableCollectionView.EditItem(Object item)
at System.Windows.Controls.DataGrid.EditRowItem(Object rowItem)
at System.Windows.Controls.DataGrid.OnExecutedBeginEdit(ExecutedRoutedEventArgs e)
etc...
I have also this problem, And found that the point here is that we can not edit a IEnumerable in a DataGrid, only a list can be edited.
therefore we didn't need to create a new class, its works also on a LINQ query with anonymous return type. it's need only to be a list.
here is a sample of my code:
dtgPrdcts.ItemsSource= ProductLists.Select(Function(x) New With {.ListTitle = x.ListTitle, .ProductID = x.ProductID, .License = "", .ForRemove = True}).ToList
I still don't know what specifically caused the problem, but I managed to work around it, and I'm not sure how much of what I did was overkill, but it works.
I created a new class just for the purpose of holding the data in the DataGrid rows. I make a List of objects of this class and fill it in and bind it to the DataGrid as I was doing before. I also added the usual stuff and nonsense for getting Change Notification to work (probably overkill) and I had to re-define a comparison function in a different way to get it to sort because of that whole comedy situation.
i.e.
List<UsablePref> MemberIngredientPrefs = new List<UsablePref>();
...
foreach (RAM_Ingredient ingredient in App.Ingredients)
{
ingredient.GetPreferences(EditorMember);
UsablePref pref = new UsablePref();
pref.Ingredient = ingredient.Ingredient;
pref.IngredientID = ingredient.IngredientID;
pref.Preference = ingredient.Preference;
MemberIngredientPrefs.Add(pref);
}
// Sort alphabetically by ingredient name,
MemberIngredientPrefs.Sort(UsablePref.CompareByName);
// and bind the ingredient prefs DataGrid to its corresponding List
dgIngredients.DataContext = MemberIngredientPrefs;
I had this same problem trying to create a list of rows from a join; since the LINQ query returns an IEnumerable, I had the DataGrid bound to that IEnumerable; this worked fine for readonly and oddly worked with ComboBoxes and some other custom controls I used, but plain text editing threw the InvalidOperationException. The solution was an ObservableCollection in place of the IEnumerable; basically from:
BoundView = (/*LINQ QUERY*/); // is IEnumerable<CustomJoinObject>
to
BoundView = new ObservableCollection<CustomJoinObject>(/*LINQ QUERY*/);
In both cases BoundView is the DataContext for the DataGrid.
I'm assuming this happens because IEnumerable doesn't have the machinery to support a datagrid, whereas ObservableCollection does.
The model class needs to implement the interface INotifyPropertyChanged coming from the namespace System.ComponentModel.
Class example:
public class Exemple : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion INotifyPropertyChanged Members
}
even if this thread is old, it could help someone.
The issue is that LINQ returns a IEnumerable, as said VaelynPhi, but the cause is that editing a datagrid requires a source that implements IEditableCollectionView.
You can then use a list, an observableCollection or any collection implementing this interface.
I found this solution thanks to Shoe who gave an answer on this tread.
In my case this exception was thrown when I wanted to edit some cells. The problem was of wrong collection type bound to ItemSource => when I switched from IEnumerable<T> to ObservableCollection<T> everything works correctly.

Binding ItemsSource to an XML String

I have a database stored procedure call that, among other columns, returns a column that contains data in XML format. I'm trying to display the results of this call in a grid (the call is made through Entity Framework, so the actual objects bound to the grid are POCO's, and the column in question is a string property).
While the normal columns display correctly, I need to be able to take the XML data in that one column and bind its contents (it will contain multiple nodes) to an ItemsControl within the template for the cell.
For example, let's say I have a grid that displays a collection of the following object:
class Photo
{
string PhotoId { get; set; }
string Name { get; set; }
string TagListXml { get; set; }
}
This is intended to represent a photo, and the TagListXml property contains an XML string listing all of the tags that have been applied to the photo. Something akin to...
<PhotoTags>
<Tag>Faces</Tag>
<Tag>People</Tag>
<Tag>Sepia</Tag>
</PhotoTags>
(While obviously a normal POCO would have a List<string> or something like that, let's just assume for the moment that I must use an XML string)
In my grid, I want to be able to specify an ItemsControl that uses this XML and, ultimately, gives me items Faces, People, and Sepia.
I've tried this for a cell template:
<DataTemplate>
<ItemsControl ItemsSource="{Binding TagListXml,
Converter={StaticResource xmlConverter}}" />
</DataTemplate>
Where xmlConverter is defined as such:
<dc:StringToXmlConverter x:Key="xmlConverter" XPath="PhotoTags" />
And dc:StringToXmlConverter is a custom IValueConverter that just takes a string value, instantiates an XmlDocument and loads the string, then returns an XmlDataProvider with that new document and the XPath specified above.
While this does not produce any errors either in the form of an exception or a binding error in the Output window, it doesn't do anything (there are no results displayed).
I believe this is because an XmlDataProvider cannot be set to the ItemsSource directly, but rather must be set as the Source of a Binding. (in other words, you must do ItemSource="{Binding Source={StaticResource xmlProvider}}" rather than ItemsSource="{StaticResource xmlProvider}").
I can't seem to get anywhere with it, and I've been banging my head on this for the last couple of hours.
How can I bind an XML string to the ItemsSource of an ItemsControl?
Why not return an XmlNode[] instead of a XmlDataProvider (which is mainly for XAML anyway)?

Advice needed on WPF TreeView and lazy load databinding with NHibernate

I'm working on a project where I need to provide some treeviews to the users. So far I've managed to get my business objects and their persistence to a database fully functional.
I'm using NHibernate with SQLite3 to store the objects. The business object is as follows:
public class ErrorObject
{
public virtual int ID { get; set; }
public virtual string Description { get; set; }
private IList<ErrorObject> _errorObjects = new List<ErrorObject>();
public virtual IList<ErrorObject> ErrorObjects
{
get { return _errorObjects; }
set { _errorObjects = value; }
}
}
I'm binding this to a treeview like this:
<TreeView ItemsSource="{Binding ErrorObjects}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type DataModel:ErrorObject}"
ItemsSource="{Binding ErrorObjects}">
<TextBlock Text="{Binding Path=Description}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
What puzzles me, is the fact that, as long as I don't close the NHibernate session, I have full lazy loading out-of-the-box :o)
Can anybody advice if this is the way to continue? Or have I a flawed concept?
Seems to be a good concept.
In NET 3.5 SP1 virtualization support has been added to TreeView (by adding support for hierarchical data to VirtualizingStackPanel).
As long as NHibernate fills the child list ErrorObjects when it was requested (first access to the getter) the tree should build up with lazy loading.
That's how it's designed to work, so just be happy about it :-)
You should make the ErrorObject implement INotifyPropertyChanged/INotifyCollectionChanged if you want the UI to be updated when the collection updates, OR make the ErrorObjects property to be of type ObservableCollection<ErrorObject>, ObservableCollection<T> implements INotifyCollectionChanged and INotifyPropertyChanged`.
I think the best idea for your scenario would be use an ObservableCollection instead of the List, if I am not wrong, the ObservableCollection<T> does implement IList<T> too.

Resources