Parent-Child inside DataTemplate - c#

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.

Related

UWP Binding property to current item of ItemsSource

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.

MVVM and dynamic generation of controls

i've written a tool that generates sql queries using GUI, i want to rewrite the tool using MVVM and WPF, every sql column type has a different control as you can see in the following image
i add a column filter control based on the sql column type, and i generate the controls using code, just like i used to do in windows forms.
in MVVM i've read that the view is writtien enteirly using XAML,
does MVVM suite such application where i have to add different user
controls dynamically to a stack panel?
The controls won't exist in the view unless some column is double clicked, that means the control won't be available in the xaml and won't be hidden or collapsed.
is there any way that i can avoid the bindings in the code behind?
should i create a user control for each column type?
in general what is the best approach to devlop such application with complex and dynamic ui using mvvm?
Guess I know how to achieve that, but it is very complex stuff. First you should comprehend MVVM basic concepts.
Main ViewModel should be a class with ObservableCollection of ViewModels, each of them represents a column with its data and properties.
interface IViewModel : INotifyPropertyChanged,IDisposable
{
}
interface IColumnViewModel : IViewModel
{
}
class ViewModelBase : IViewModel
{
// ... MVVM basics, PropertyChanged etc. ...
}
class MainViewModel : ViewModelBase
{
ObservableCollection<IColumnViewModel> Columns {get; set}
}
In View I suppose something like ItemsControl with ItemTemplate, that should embed ContentControl with DataTemplate, that shall be automatically selected by WPF according to binded DataContext of list item. StackPanel itself is not suitable for that, but it can be invoked as ItemsPanelTemplate
<Window
xmlns:v="clr-namespace:WpfApplication.Views"
xmlns:vm="clr-namespace:WpfApplication.ViewModels">
<Window.Resources>
<DataTemplate DataType="{x:Type TypeName=vm:TextColumnViewModel}">
<v:TextColumnView/>
</DataTemplate>
</Window.Resources>
<ItemsControl
ItemsSource="{Binding Columns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
So, you should build View/ViewModel pair for every column type.
Hope, my example will help. Good luck with your girlfriend and MVVM :)
If I've understood your scenario correctly :
You can use Data Templates & Items Templates
For example I've written an application which loads Data into Collection and then shows each item of that collection in a Wrap Panel [ Or stack panel ] based on defined data template.
And Wrap penel items are in sync by the collection itself within two way binding
You should consider using Observable Collections to achieve this goal
Then you can fill the collection and see the results on a view
I hope this helps
To write something like this in MVVM, you would have one view that is say, your content area. That view would have a view model, one of the properties of that view model would be a view, or several properties of that view model would be a view. It takes a bit to wrap your head around at times, but if you use Inversion of Control and Dependency Injection properly a view of views is very manageable in an MVVM pattern.
Well, your view isn't written entirely in XAML - you generate controls in C#.
I don't think you'll gain something from rewriting this and fitting it into an MVVM mold. Just keep the code as it is now and enjoy.

ListBox of anonymous UIElements

I want to create a ListBox and fill it with anonymous UIElement. In other words, the DataTemplate of the ItemTemplate will contain only one element, and afterwords during the runtime, afterwards I will create in the code behind different UIElements (TextBlocks, Grids ...) and populate the list with it.
So how am I going to write the DataTemplate of the ListBox? And how am I intend to use an ObservableCollection for the ItemSource? So should use an ObsevableCollection of UIElement?
First Question: No DataTemplate. Since your items are already UIElements, you don't need a DataTemplate.
And if you're creating a changeable collection, then yes, ObservableCollection<UIElement> is the way to go.
Now, why would you be doing this? You may want to ask yourself if this is the best way of doing things. Why isn't your data and your presentation separated? If you need more than one type of element in the list, will DataTemplateSelector allow you to have a real ViewModel?
<ListBox x:Name="name" ItemsSource="{Binding source}">
<ListBox.ItemTemplate>
<DataTemplate>
<textbox x:Name="name"></ToggleButton> //or any tool
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Bind multiple views to multiple viewmodels

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.

MVVM binding content control from observablecollection of views

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.

Categories