I use a range of viewmodel classes in my application to represent the data. I define 2 or more "ViewPresenter" controls (based on the ContentPresenter) to show these viewmodels in for instance "minimal", "summary" or "detail" modes. Each of the presenter classes use a resourcedictionary to define the datatemplates for all the viewmodels it needs to be able to show.
Now here's the problem: When different types of these viewpresenters are contained within each other, they inherit datatemplates from their containers, which leads to some issues. How can I stop that inheritance from happening?
Edit: Example for illustration
// first ContentPresenter
<ViewPresenter DataContext="{Binding DerivedObj}">
<ViewPresenter.Resources>
<DataTemplate TargetType="{x:Type DerivedClass}">
<DerivedClassView>
// Nested ContentPresenter
<ViewPresenter DataContext="{BaseObj}">
<ViewPresenter.Resources>
<DataTemplate TargetType="{x:Type BaseClass}"/>
</ViewPresenter.Resources>
</ViewPresenter>
</DerivedClassView>
<DataTemplate/>
</ViewPresenter.Resources>
</ViewPresenter>
So the problem that occurs here is that the nested template is less specific than the one in the higher level contentpresenter, and so it is never shown. The nested contentpresenter needs to start with a clear resourcedictionary, and refrain from using the higher contentpresenter's datatemplates.
Related
I've seen a ton of examples where a content control's ContentTemplateSelector property is assigned a StaticResource.
Example: <ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource MyTemplateSelector}"/>
MSDN states:
Typically, you create a DataTemplateSelector when you have more than
one DataTemplate for the same type of objects and you want to supply
your own logic to choose a DataTemplate to apply based on the
properties of each data object.
Source: DataTemplateSelector Class
Knowing this - my situation is that I have two types of objects where each object has its own set of data templates it can use. Is there any way I can simply have the content control's ContentTemplateSelector bind to a ContentTemplateSelector property on the view model? The tricky part is that the data templates are defined in the xaml file - I can't just initialized a new instance of the specific ContentTemplateSelector for that class.
Additional info: I have a parent view model where each of its subclasses should be represented as a different type of object. So depending on which subclass view model is toggled, it should use its respective ContentTemplateSelector and data templates.
I've also gotten the above to work with a converter, but I want to stay away from this. Ideally, I'd like to have code that makes this process as general as possible. i.e. Not needing to maintain the converter code to add new types of objects in the future just to return the right DataTemplateSelector. The case should be that anytime a new subclass is added, it'll just work right away.
I am an intermediate WPF developer, with working knowledge on how to implement dependency properties as well as simple custom controls. I do not yet understand how I can add a DataTemplate dependency property to a custom control, and use it to define the element tree for each datum in a collection of data.
The full story is that I have been working on creating a WPF map control that displays many different points and geometric shapes on the map, over map tiles. These shapes will translate with the rest of the map when user "drags" the map around.
I have accomplished this, insofar that I have created the map control, and can add child elements to it in Xaml that have map coordinates. I would like to take this farther, and add properties for collections of data, i.e. points, areas, etc. To better understand what I'm looking for, I would like to re-create two properties from ListBox: ItemsSource and ItemTemplate.
I have added two dependency properties to my Map control - PointsSource and PointsTemplate. PointsSource is of type IEnumberable and represents the collection of data to display on the map. PointsTemplate represents what each of those datum should look like. Simply throwing these properties into my control is obviously not enough, but I am unsure of how to coordinate them with one another. If anyone has working knowledge of creating a custom data control with it's own DataTemplate properties for changing the UI tree for each data element, I would really appreciate it.
I have found what I am looking for in the DataTemplate itself. The DataTemplate provides a function for code behind called LoadContent(). LoadContent produces a dependency object that represents the tree of content for a given datum. From what I have found elsewhere, the common use for LoadContent might look like the following:
foreach (object point in PointsSource)
{
FrameworkElement pointElement = _PointsTemplate.LoadContent() as FrameworkElement;
pointElement.DataContext = point;
this.Children.Add(pointElement);
}
The above code will add a content tree for every single element of data, and we give it the datum to bind its DataContext to.
If anyone has working knowledge of creating a custom data control with it's own DataTemplate properties for changing the UI tree for each data element, I would really appreciate it.
Basically, you will want to use an ItemsControl inside your control template, and bind its ItemsSource and ItemTemplate properties to your custom Dependency Properties. Ie,
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<ItemsControl ItemsSource="{TemplateBinding PointsSource}"
ItemTemplate="{TemplateBinding PointsTemplate}"
/>
</ControlTemplate>
</Setter>
</Style>
(assuming the DPs IEnumerable - "PointsSource" and DataTemplate - "PointsTemplate")
I've created a custom control that is styled and configures in its own XAML sheet. Databindings in this control uses a specific object (CProject class).
Just to clarify, the control is a project frame, that has controls for settings and a canvas that will be the workspace for each/any project.
The project control (IPProjectPanel) inherits "Frame", and also adds a "settings" stack panel to its children list which in turn contains controls for - well, settings.
The CProject class however, is the pure functional part, with no UI interaction or handling whatsoever. So, I need to "plug" an instance of CProject into every unique project that can be active. So, I want to set a specific instance of CProject as datacontext to every IPProjectPanel instance in a tabpanel. Either I want to set the datacontext by code, or have it created by settings datacontext in XAML, and retrieving it after it has been initialized.
The problem though, is that I cant quite figure out either.
Here is a snippet of the style of the IPProjectPanel in XAML, that uses the approach to set datacontext in XAML:
<Style TargetType="{x:Type ip:IPProjectGrid}">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ip:IPProjectGrid}">
<Grid Background="White"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="0">
<!---->
<Grid.DataContext>
<ipp:CProject></ipp:CProject>
</Grid.DataContext>
<StackPanel x:Name="PART_settingsPanel"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
MinWidth="300" Background="Gray">
<GroupBox Header="Project settings">
<StackPanel>
....
</style>
Here it is set as a context to Grid, but I'd like to have it as a context of the actual class (IPProjectPanel).
So, the IPProjectPanel instance is created by code (for now..), and I need to retrieve the CProject instance (or set one) so that I can work with it.
I'd like to keep to C#/WPF ways to do stuff, as this app is also training for WPF and C# concepts and such. So the "best C#-WPF" way to do it, is very welcome, but a solution either way!
Thank you for your time.
So in general, the datacontext is primary inteded to be for your ViewModel, and in fact WPF is really set up for doing MVVM (Model View ViewModel) style applications. It's actually fairly simple to learn, but if you're looking for the "Best C#-WPF" way of doing things, take the time to learn MVVM. It's really fairly straightforward.
Simple Example from CodeProject:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
From Microsoft (somewhat heavy reading):
http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx
The way you have this set up currently has the potential to create some nasty bugs. You should never declare a DataContext object instance inside a template unless you never plan on accessing it outside of that template's scope. By doing so you will be creating a new instance of the CProject class any time the control needs to be visually re-loaded (like changing tabs) and you may end up referencing an old CProject instance in code while displaying a completely separate one on the screen. Declaring a DataContext object not in a template (i.e. Window.DataContext) is fine.
If you want each control instance to create its own CProject instance you would be better off doing that in code in the constructor and exposing that as a property on the control which you can then bind your Grid.DataContext to inside the template. Avoid setting it to the DataContext property of the control itself as this will cause any implicit source Bindings that are set on the control where it is declared in XAML to break by overriding the inherited DataContext:
Grid.DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PropertyWithCProject}"
It's probably more likely that you will want to control the CProject instances externally and hand them to the control instances. To do this you can create them in a container ViewModel class (MVVM pattern) and set this as a DataContext higher up on something that will contain all of your custom controls. You can then expose individual CProjects or a collection of them and bind your controls' DataContexts to those.
I have a WPF window displaying different self-defined Views. So far I was able to use everything I learned about MVVM :)
Now I got to a new "problem": I have 10 entities of the same view in a bigger view. These ten view-entities contain a set of controls (textbox, combobox etc.) but are all consistent.
So how do I bind these Views to a ViewModel?
I thought about having 10 instances of the ViewModel in the "higher-level" ViewModel and give the views fix-defined the instances of the VM as datacontext.
My question is now --> Is there a easier (or more convienient) way to bind many (identical) views to their viewmodels?
Code-Example:
View Model:
private PanelViewModel _panelViewModel1 = new PanelViewModel();
public PanelViewModel PanelVM1
{
get { return _panelViewModel1; }
}
View-Example:
<myControls:vwPanel HorizontalAlignment="Left" x:Name="vwPanel1"
VerticalAlignment="Top" DataContext="{Binding Path=PanelVM1}"/>
What bothers me is that I would need this logic ten times for ten views?
UPDATE:
To answer some questions: I want to show one view 10 times (in my example) I defined my own view by inheriting from UserControl. So my vwPanel inherits from UserControl. The 10 vwPanels are just placed inside a StackPanel inside a Grid.
It's not about displaying data, as you pointed out, there would be a listview or a datagrid a better place to start. It's a special case where I need this much input-controls :/
UPDATE2: What I hoped for was more like defining a List of ViewModels and Bind my 10 Views to one of this List. But this will not work will it? At least I wouldn't know how to refernce one "special" entitiy in the list out of XAML...
Typically I use implicit DataTemplates for mapping Views to ViewModels. They can go in <Application.Resources>, <Window.Resources> or even in under specific elements only such as <TabControl.Resources>
<DataTemplate DataType="{x:Type local:PanelViewModel}">
<myControls:vwPanel />
</DataTemplate>
This means that anytime WPF encounters an object in the VisualTree of type PanelViewModel, it will draw it using vwPanel
Objects typically get placed in the VisualTree through an ItemsSource property
<ItemsControl ItemsSource="{Binding CollectionOfAllPanels}" />
or by using a ContentControl
<ContentControl Content="{Binding PanelVM1}" />
If I understand your question correctly, you have a collection of something that you what to represent visually. That is, you have several viewmodels that you want to define a single view for, but show X number of times. Your example shows you using a panel as your view for the "PanelViewModel"...what is the parent item's control for the vwPanel? Assuming you're using something like a ListBox, you can define a custom DataTemplate that contains your vwPanel and assign that DataTemplate to your ListBox.ItemTemplate.
For example:
<Window.Resources>
<DataTemplate x:Key="myVMTemplate" TargetType="{x:Type myViewModels:PanelViewModel}">
<myControls:vwPanel HorizontalAlignment="Left" VerticalAlignment="Top"/>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Path=MyCollectionOfPanelVMs}"
ItemTemplate="{StaticResource myVMTemplate}" />
I haven't verified that this works.
I using from mvvm in my application. I want know how to define my user control in mvvm pattern.
Must I define it by using from mvvm, or I can define it generally?
Let's just call the control that embeds the user control MainWindow, and the user control UserControl. Since you are in MVVM pattern, you have at least one View Model for the outer view - I usually use the name MainVm.
You have two choices for the user control: They can share the same View Model, or you could have a sub view model, just for the UserControl, i.e. UserVm.
For your first choice, you do nothing. You define UserControl (Visual Studio 'add new item' -> User Control is a pretty good start). Then, you simply embed it in Main Window.
<Window
x:Class="SO.MainWindow"
...
xmlns:src="clr-namespace:SO"
...
>
...
<src:UserControl />
...
</Window>
UserControl will inherit the same DataContext from MainWindow, and do all the {Binding} as you would do in the MainWindow.
If you want to have a sub view model (UserVm) - it would typically be a public property of the MainVm (say, userVm). In that case, you'll set the DataContext of the UserControl when you reference it.
<src:UserControl DataContext="{Binding Path=userVm}" />
Another popular paradigm would be to declare the DataTemplate instead of the UserControl. If you do that, you just need to put the UserVm (either instantiate it in the XAML, or through binding):
<Window x:Class="MainWindow" ...>
<Window.Resources>
<DataTemplate x:Key="UserDt"> <!-- or user TargetType instead of x:Key -->
...
</DataTemplate>
</Window.Resources>
...
<!-- You can put in a ContentControl like here: -->
<ContentControl Content="{Binding Path=userVm}"
ContentTemplate="{StaticResource UserDt}" />
<!-- or, if you defined TargetType for the DT, you can simply instantiate
the sub VM here. I don't like this apporach but it exists. -->
<src:UserVm />
</Window>
I think that depends on the user control. The user control can be just a view, in which case you would compose a larger control or page which has this user control as part of the whole. The larger control or page would provide the view and the view model parts for this view.
Or you could create a self contained user control which has all of mvvm and use events to interact with the larger user control that it is a part of.
I suspect you'll get better reuse and modularisation with the second approach.
In short: it depends.