The type{0} does not support direct content - WPF / XAML - wpf

I defined in my code two classes: a "Person" class with public "Age" and "Name" property, and a "People" class that inherits from Generic.List(of T).
The code for People class is as followed:
Public Class People
Inherits Collections.Generic.List(Of Person)
...
End Class
What I want to achieve is to directly initialize the People class, and add individual Person to it in XAML, i.e.:
<local:People x:Key="Familty">
<local:Person Age="11" Name="John" />
<local:Person Age="12" Name="John2" />
...
</local:People>
But I keep getting an error in XAML saying:
The type 'People' does not support direct content.
Any idea as for how to solve this problem?
Thank you very much!

What exactly do you want to do?
It seams that you try set a content to a control (that must be a ContentControl / or inherited class). Also please notice that you are setting the Content in xaml, that means the it must be a UIElement at least.
If you want to represent a list of people, please set a dataTemplate to that dataType and have a visual representation, then set the ItemsSource (of People which should be a items Control) to a list (or observable collection) of people.
You should consider separating the UI from the model.
So, what exactly are you trying to do ?

Related

Nested Data Context with Unity

I took a course on VB.Net + WPF at university last year. For the final project, I decided to give MVVM a go (we hadn't discussed it at all in the course, I had just researched it and thought it would be a useful exercise). It was a good experience however I'm rather sure I might have made some poor choices when it came to design.
I've since graduated and my job has nothing to do with WPF or Windows development however I'm developing a small application in my own time and thought it would be fun to use C# and WPF (C# is a language I very much like to work with and I enjoyed working with WPF so it's a pretty logical choice).
Anyway, I'm using this as an opportunity to learn more about MVVM and try and implement it in a better way than I did previously. I've done a bit more reading and am finding it a lot easier to graph than I had when trying to implement it alongside learning WPF.
I've used In The Box MVVM Training as a guide and will be using Unity for dependency injection at this.
Now, in the sample app developed in the guide, there is a single view model (MainWindowViewModel). The MainWindow is pretty much a container with 3 or 4 UserControls which all share the DataContext of the MainWindow.
In my app, I'd like to have a tab-based interface. As such, the MainWindow will be primary concerned with displaying a list of buttons to switch the current view (i.e. move from the 'add' view to the 'list view'). Each view will be a self-contained UserControl which will implement it's own DataContext.
The same code in the app is as follows:
MainWindow window = container.Resolve<MainWindow>();
window.DataContext = container.Resolve<MainWindowViewModel>();
window.Show();
That's fine for setting data context of the MainWindow, however how will I handle assigning each user context it's own ViewModel as a DataContext?
EDIT: To be more specific, when I say tab-based interface, I don't mean it in the sense of tabs in a text editor or web browser. Rather, each 'tab' is a different screen of the application - there is only a single active screen at a time.
Also, while Slauma's post was somewhat helpful, it didn't really explain how I'd go about injecting dependencies to those tabs. If the NewStatementView, for example, was required to output it's data, how would I inject an instance of a class that implements the 'IStatementWriter' interface?
EDIT: To simplify my question, I'm basically trying to figure out how to inject a dependency to a class without passing every dependency through the constructor. As a contrived example:
Class A has Class B.
Class B takes as a constructor paramater needs an implementation of Interface I1.
Class B uses Class C.
Class C takes as a constructor paramater needs an implementation of Interface I2.
How would I handle this scenario using DI (and Unity)? What I don't want to do is:
public class A(I1 i1, I2 i2) { .... }
I could register everything using Unity (i.e. create I2, then C, then I1 and B, and then finally insert these into A) but then I would have to instantiate everything when I want to use A even if I might not even need an instance of B (and what if I had a whole bunch of other classes in the same situation as B?).
MVVM has lots of benefits, but in my experience wiring up the view models and the views is one of the biggest complexities.
There are two main ways to do this:
1:
Wire the view models to the views.
In this scenario, the XAML for the MainWindow contains the child controls. In your case, some of these views would probably be hidden (because you are only showing one screen at a time).
The view models get wired to the views, usually in one of two ways:
In the code behind, after the InitializeComponents() call or in a this.Loaded event handler, let this.DataContext = container.Resolve<MyViewModelType>();
Note that in this case the container needs to be globally available. This is typical in applications that use Unity. You asked how children would resolve interfaces like IStatementWriter. If the container is global, the child view models could simply call container.Resolve<IStatementWriter>();
Another way to wire the view models into the views is to create an instance of the view model in XAML like this:
<UserControl ...>
<UserControl.DataContext>
<local:MyViewModelType/>
</UserControl.DataContext>
...
</UserControl>
This method is not compatible with Unity. There are a few MVVM frameworks that allow you to resolve types in XAML (I believe Caliburn does). These frameworks accomplish this through markup extensions.
2:
Wire the view up to the view model.
This is usually my preferred method, although it makes the XAML tree more complicated. This method works very well when you need to perform navigation in the main view model.
Create the child view model objects in the main view model.
public class MainViewModel
{
public MyViewModelType Model1 { get; private set; }
public ViewModelType2 Model2 { get; private set; }
public ViewModelType3 Model3 { get; private set; }
public MainViewModel()
{
// This allows us to use Unity to resolve the view models!
// We can use a global container or pass it into the constructor of the main view model
// The dependencies for the child view models could then be resolved in their
// constructors if you don't want to make the container global.
Model1 = container.Resolve<MyViewModelType>();
Model2 = container.Resolve<ViewModelType2>();
Model3 = container.Resolve<ViewModelType3>();
CurrentViewModel = Model1;
}
// You will need to fire property changed notifications here!
public object CurrentViewModel { get; set; }
}
In the main view, create one or more content controls and set the content(s) to the view models that you want to display.
<Window ...>
...
<ContentControl Content="{Binding CurrentViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelType}">
<local:MyViewType/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelType2}">
<local:ViewType2/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelType3}">
<local:ViewType3/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
...
</Window>
Notice that we tie the child views to the view models through data templates on the ContentControl. These data templates could have been defined at the Window level or even the Application level, but I like to put them in context so that it's easier to see how the views are getting tied to the view models. If we only had one type of view model for each ContentControl, we could have used the ContentTemplate property instead of using resources.
EDIT: In this method, the view models can be resolved using dependency injection, but the views are resolved through WPF's resource resolution mechanism. This is how it works:
When the content for a ContentPresenter (an underlying component in the ContentControl) is set to an object that is NOT a visual (not derived from the Visual class), WPF looks for a data template to display the object. First it uses any explicit data templates set on the host control (like the ContentTemplate property on the ContentControl). Next it searches up the logical tree, examining the resources of each item in the tree for a DataTemplate with the resource key {x:Type local:OBJECT_TYPE}, where OBJECT_TYPE is the data type of the content. Note that in this case, it finds the data templates that we defined locally. When a style, control template, or data template is defined with a target type but not a named key, the type becomes the key. The Window and Application are in the logical tree, so resources/templates defined here would also be found and resolved if they were not located in the resources of the host control.
One final comment. If a data template is not found, WPF calls ToString() on the content object and uses the result as the visual content. If ToString() is not overridden in some meaningful way, the result is a TextBlock containing the content type.
<--
When you update the CurrentViewModel property on the MainViewModel, the content and view in the main view will change automatically as long as you fire the property changed notification on the main view model.
Let me know if I missed something or you need more info.
For a Tab-based interface this classical article about MVVM pattern in WPF might be very useful. (It also offers a downloadable sample application.)
The basic idea to connect each tab with a UserControl is as follows (only a rough sketch, details are in the article):
The MainWindow View has a ContentControl ...
<ContentControl Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}" />
... which binds to a collection of "Workspaces" in the MainWindowViewModel:
public ObservableCollection<WorkspaceViewModel> Workspaces { get; private set; }
This WorkspaceViewModel serves as a base class for all ViewModels you want to display as a tab.
The WorkspacesTemplate is a DataTemplate which binds a TabControl to the collection of WorkspaceViewModels:
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}" />
</TabControl>
</DataTemplate>
And for every specific Tab you have a UserControl with a ViewModel which derives from WorkspaceViewModel ...
public class MySpecialViewModel : WorkspaceViewModel
... and which is related to the UserControl by a DataTemplate:
<DataTemplate DataType="{x:Type vm:MySpecialViewModel}" >
<v:MySpecialUserControl />
</DataTemplate>
Now, if you want to open a tab you would have a Command in the MainWindowViewModel which creates the ViewModel belonging to that tab and add it to the Workspaces collection of the MainWindowViewModel:
void CreateMySpecialViewModel()
{
MySpecialViewModel workspace = new MySpecialViewModel();
Workspaces.Add(workspace);
}
The rest is done by the WPF binding engine. The TabControl recognizes automatically that this special workspace item in the collection is of type MySpecialViewModel and selects the right View/UserControl through the DataTemplate we have defined to connect ViewModel and View and displays it in a new Tab.
At the point where you resolve your Views deriving from UserControl, use property injection to resolve a new ViewModel for each one and set the DataContext property of the view to it.

DataGrid binding Type issue

Here's my setup. I have the following BusinessObject classes
BaseClass
InheritClassA : BaseClass
InheritClassB : BaseClass
InheritClassC : BaseClass
I also have the following dictionary
Dictionary<classType is a String, ObservableCollection<BaseClass>>
I want to be able to, in my converter, base on the type of object I select, return the proper ObservableCollection so I can bind it to my datagrid, and autoGenerateColumn on so I can view all my properties on the grid.
However, when i just retrieve my collection back as BaseClass, it only shows me the columns for the base class. I think this is because the Type for the ObservableCollection is type of baseClass and not specific to InheritClassA/InheritClassB/InheritClassC
Is there a way to dynamically create a ObservableCollection's Type? So I can create ObservableCollection and assign casted values into it?
Is there a proper way to bind it to my datagrid so I can view the properties for the inherit class?
Thanks very much ,
Create a dictionary that has as the value-type an Object and not a BaseClass. Then you can add the concrete ObservableCollection<InheritClass[A,B,C]> and the binding will work like you expect:
Dictionary<classType is a String, ObservableCollection<object>>
If you want to be more restrictive, declare it as Dictionary<string,IEnumerable>

how to link container and its contents?

i have an object based on ContentControl type and I want to embed custom controls into its content. below is the code.
the problem is that i need MyContainer to have a list of MyControl objects so that it can communicate to them, and each MyControl will need a reference to its MyContainer.
how is this done properly? one way that i see is to declare an attached property on MyControl and set it to the name of the MyContainer object, but this seems redundant because MyCOntrol objects can search the visual tree to find the container. if searching is the right way to do this, where would i place the code that does the search? in MyControl constructor?
thanks for any input
konstantin
public class MyContainer : ContentControl
{
...
}
public class MyConrol : Control
{
...
}
<c:MyContainer>
<Grid>
<c:MyControl />
</Grid>
</c:MyContainer>
You can add property MyControls to MyContainer class, create a template for MyContainer with a list in it (ItemsControl, ListBox or some other list control), put the list itself inside the grid from your sample code, bind the list's ItemsSource to MyControls property.
To get container for the control in XAML, you can use binding with RelativeSource set to FindAncestor.
If you need to find container from code, you should probably do it every time or cache the value on the first use (can controls be moved to another container?). Contructor is not the appropriate place, because first control is created and only then it is put into the tree.
Attached properties are definitely unnecessary.

WPF: How to reuse controls when ObservableCollection is concrete?

I have a object that inherits from TabItem. I have a bunch of Database objects that will reuse the same code so I wanted only one TabItem class and then use DataTemplates to control how each object gets presented.
Problem is that the TabItem shows a collection of Objects and ObservableCollection is concrete.
I've pondered a few solutions but none of them work. Seems like I will have to create one class for each object type even when they will all be the same (except for ObservableCollection having different types). That's not very DRY.
I can't make a UserControl generic, I can't let the UserControl constructor take in a generic class ( unless I define the Type wihc I don't wan't to do). I guess creating a base UserControl class and then inheriting that will have to do. Does it inherit the XAML code as well or will I have to rely on styles and templates?
Am I missing something?
Look into using DataTemplateSelector to provide flexibily in how you present your data in WPF.
Here are three sites that helped me:
Data Templating Overview
How to use DataTemplateSelector to alter views of objects in a ListBox
WPF Tutorial - How To Use A DataTemplateSelector

WPF: Binding DataGrid to a list<Product> having a DataGridComboBoxColumn bound to a list<Category>?

I have a DataGrid with ItemsSource set to a list of products and
I have a DataGridComboBoxColumn inside the DataGrid with ItemsSource set to a list of categories. That way I want the user to choose a certain category for each product.
I always get the binding error:
BindingExpression path error: 'Categories' property not found on 'object' ''Product' (Hash)
Well I do not want to make the Category list part of the Product entity as 1:N relation, although it would work that way.
I want to keep them separate.
Anyone knows a workaround?
Create class with static property like
static class ValueLists
{
public static IEnumerable<Category> Categories {get {... }}
}
and use following binding
ItemsSource="{x:Static myNs:ValueList.Categories}" />
this is kind of late reply but in order to share the knowledge I found this:
Binding a WPF DataGridComboBoxColumn with MVVM
This answer shows that is not always mandatory to convert the second list to a static class, you can always specify a RelativeSource and search for an specific Ancestor and then bind to the "other" list you have in your ViewModel.
This is probably relevant to your problem.
What is happening here?
The Columns collection is just a property in the Datagrid; this collection is not in the logical (or visual) tree, therefore the DataContext is not being inherited, which leads to there being nothing to bind to.

Resources