I have an ItemsControl whose ItemsSource gets bound to an ObservableCollection<Component> at run time. I have defined a data template for type Component which works fine.
Now Component has a ObservableCollection<Control> and I want to add another ItemsControl inside my Component Datatemplate to render all the controls. Control here is my own custom object not related to a wpf control.
There are different types of controls so I am trying to use an ItemTemplateSelector to select the right template for each type. In the example below to keep it small I have only shown one of the templates "RWString" which I find using a FindResource in MyControlTemplateSelector overriding SelectTemplate. But the SelectTemplate never gets called(using a breakpoint to check). Is there something wrong in my xaml?
<ItemsControl.Resources>
<src:MyControlTemplateSelector x:Key="XSelector" />
<DataTemplate DataType="{x:Type src:Component}" >
<Expander Visibility="{Binding Path=Show}">
<ItemsControl ItemsSource="{Binding Path=Contrls}"
ItemTemplateSelector="{StaticResource XSelector}">
<ItemsControl.Resources>
<DataTemplate x:Key="RWstring" >
<TextBlock Text="{Binding Path=Label}"/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><WrapPanel /></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.Resources>
Update: Contrls is not a typo, its just me using a silly naming system. Contrls is a property of Component of type ObservableCollection<Control>. Also the reason I am trying to use the the ItemsTemplateSelector is that the ObservableCollection<Control> contains objects of generic types like Control<int> Control<string> etc all deriving from Control and apparently you cant create datatemplates referring to generic types.
Update3: Removed update 2 as it was unrelated. I got the ItemTemplateSelector to work by changing StaticResource to DynamicResource. But I don't know why this works...
I'm guessing this doesn't work with a StaticResource as the Resource is inside the ItemsControl which probably has not been created at load time when StaticResources are evaluated.
DynamicResources at load time are evaluated to an expression at load time and then evaluated to the correct value when requested.
Try move the Resource outside of the ItemsControl.
In the line where you bind the nested ItemsControl, is the Path correct? It is currently "Contrls", should it be "Controls"?
Related
I got the following setup for my page:
<ListView ItemsSource="{Binding RecentResults}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Background="Transparent" Orientation="Horizontal" ItemWidth="400" ItemHeight="300" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<ui:CarResultControl Result="{Binding}" Padding="0" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I bind to a local property which provides my results asynchronously. This works perfectly fine.
Now my CarResultControl binds to it's local Result property (which is an DependencyProperty). When I specify Result={Binding} then the property get's updated but not as expected with the CarResult but CarResultControl. If I use Result={Binding Path=.} or similiar, it just doesn't update at all.
As far as I'm used to it in WPF {Binding} actually bound to the data object itself, not the control you´re attaching the binding to. Interesting is that IntelliSense actually shows the properties of the expected CarResult object when I have to choose a binding path for Result.
Is UWP doing it's own thing here again, am I just a fool and doing it wrong or is this really a bug? I struggled for long enough and can't find any info about it.
This actually should be working the way you expect unless you are changing the DataContext of the CarResultControl.
UserControls actually work in a bit weird way - when you set this.DataContext = this in the constructor, then the binding on properties will actually be relative to the UserControl itself.
To fix this you must make sure not to change the DataContext of the control itself. The easiest way is to add x:Name to the root Grid (or whatever control you have in the content of the UserControl and then set its DataContext:
RootGrid.DataContext = this;
This way you will the data context of the RootGrid will be set to the control itself, but the user control's properties will be bound to the data context relative to where the user control is, here to the list item.
I have a ViewModel that has a List of objects that have the same interface and to show them in the view we have a list of UserControls in our view model that the view binds to. I was wondering if I could create a template for the different types of concrete objects in the list and have WPF apply the correct one for me instead of creating the UserControl list and binding to that.
Basically I have:
<StackPanel
Margin="0,0,20,0"
>
<my2:ProfileIdentificationView />
<ItemsControl
ItemsSource="{Binding Path=ProfileSections}">
<ItemsControl.ItemTemplate>
<DataTemplate>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DockPanel>
Where the ItemsSource binding is the list of user controls. I am looking for a way to apply the look of those UserControls here based on the type of the ProfileSection. Like one type is an IIS section and another Type is a Users section.
You can create implciit DataTemplates for your view models via the DataTemplate.DataType. Make sure to use the x:Type markup extensions as strings are interpreted as XML-element tags (to allow implicit templating of XML).
I'm new to WPF and using MVVM. I have a view in which I want to display different content according to what a user selects on a menu. One of those things is another user control Temp which has a view model (TempVM) so I am doing this:
<ContentControl Content="{Binding Path=TempVM}"/>
and TempVM (of type TempViewModel)is null until the user clicks a button. Its data template is this
<DataTemplate DataType="{x:Type vm:TempViewModel}">
<view:Temp />
</DataTemplate>
That's fine, but the other thing I want to do is show a listbox when a user clicks a different menu item. So I am trying to do
<ContentControl Content="{Binding Path=Missions}"/>
(Missions is an observable collection of MissionData) and trying to template it like this:
<DataTemplate DataType="{x:Type ObservableCollection(MissionData)}">
<StackPanel>
<ListBox ItemsSource="{Binding}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go"/>
</StackPanel>
</DataTemplate>
But the compiler doesn't like the type reference. If I try doing it by giving the template a key and specifying that key in the ContentControl it works but obviously I see the ListBox and button when there's no Missions. Obviously I could make a user control and viewmodel and follow the same pattern as I did for the TempVM but it seems over the top. Am I going the right way about this and what do I need to do?
From what i see is that you try to use a Collection as a dataobject which is in my opinion bad practice. Having a DataTemplate for a collection is also problematic, like you already have witnessed. I would advice you to use a ViewModel for your missions collection.
class MissionsSelectionViewModel
{
public ObservableCollection<Mission> Misssions;
public MissionData SelectedMission;
public ICommand MissionSelected;
}
and modify your datatemplate to
<DataTemplate DataType="{x:Type MissionsSelectionViewModel}">
<StackPanel>
<ListBox ItemsSource="{Binding Missions}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go" Command="{Binding MissionSelected}/>
</StackPanel>
</DataTemplate>
If I were to follow your pattern of implicit templates, I would derive a custom non-generic collection MissionDataCollection from ObservableCollection<MissionData> and use it to keep MissionData items. Then I would simply reference that collection in DataType. This solution gives other advantages like events aggregation over the collection that are useful.
However, it seems to me that the best solution is the following.
Add a IsMissionsListVisible property to your VM.
Bind the Visibility property of the ContentControl showing the list to the IsMissionsListVisible property.
Use a keyed DataTemplate resource.
Implement the logic that determines if IsMissionsListVisible. Supposedly it should be true when there is at least one mission in the selected item. But the logic may be more complex.
I would do it this way. In fact, I do it this way usually, and it gives several benefits. The most important is that I can explicitly control the logic of content visibility in various situations (e.g. async content refresh).
I am attempting to populate a ScrollerViewer control with an arbitrary number of of UserControls (Views) whilst using the MVVM pattern and bindings.
I am using an ObservableCollection to maintain my View collection and I have this collection set as the datacontext for my ScrollViewer control, however, getting the views to appear in the scroll viewer has had me going round in circles for a while now.
Can someone please point me to either a suitable example, or kindly provide an example which demonstrates the functionality I am attempting to achieve here?
Many thanks,
First off, I think you want an ItemsControl, not a ScrollViewer. Once you do that, assuming that your ObservableCollection of viewmodels is called "Items":
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyControl DataContext="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Replace the <uc:MyControl DataContext="{Binding}"/> with a reference to your UserControl.
In my MainWindow, I've got a ListBox whose ItemsSource is bound to an ObservableCollection of Layout POCO objects:
<ListBox x:Name="ListBox" ItemsSource="{Binding Layouts}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
Also, in the MainWindow, we define the visual representation of a Layout object using a UserControl (LayoutUserControl):
<DataTemplate DataType="{x:Type local:Layout}">
<local:LayoutUserControl />
</DataTemplate>
When we add objects to the Layouts collection, we see new LayoutUserControls populate the ListBox. This is pretty straight forward.
Now, what I'd like to do, is somehow communicate from the MainWindow to the individual LayoutUserControls. Specifically, from the MainWindow, I want to call a single method on each of the LayoutUserControls... If I attempt to iterate through the ListBox's Items collection, all I get is a reference to the Layout objects, not the LayoutUserControls. Since the LayoutUserControls are defined in a DataTemplate, I don't have a named reference to access them...
Is there a WPF construct that supports this type of interaction from parent to child controls? RoutedEvents were my first thought, but they only support child to parent (bubble) communication. Perhaps commands are the way to go?
Any help would be appreciated.
Thanks!
Yes, there is a method which you should never use, it's the ItemContainerGenerator.
You should, as you noted yourself, set up the communication differently, commands sound reasonable. Expose a command on the Layout and bind the UserControl to it. Or create an event and make the UserControl subscribe to it, then you can raise that.
In Silverlight there is an extension called GetItemsAndContainers as described in this SO question that does this but I cannot find an equivalent in WPF.
However it may still be possible as described in How can I access the ListViewItems of a WPF ListView? by using the VisualTreeHelper to get the LayoutUserControls inside the ListBox.