MVVM solution for wpf - c#

Here's the situtation:
Data Context of the window is: MainViewModel.
It's built from ObservableCollections of SubViewModel.
Each SubViewModel has its own ObservableCollection of type String.
Now, I have a treeview. The ItemsSource is the ObservableCollection of the MainViewModel.(Means it is the SubViewModel collection).
I want that if an item is selected, then there will be displayed the ObservableCollection(type String) of the selected Item in the treeview.
How can I do that?
Some code:
<TreeView ItemTemplate="{DynamicResource TreeViewDataTemplate}" ItemsSource="{Binding SubViewModelCollection}"/>
I want to display the collection in a stack panel because of some reasons.
So: (TypeCollection is the string ObservableCollection of the item, it is currently not working of course)
<ItemsControl ItemsSource="{Binding TypeCollection}" x:Name="UserList" ItemTemplate="{StaticResource TemplateDataTemplate}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Grid.Column="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
I've been struggling alot with that, how can I achieve my target?

Bind the treeview ItemsSource to the UserList's SelectedItem.
<TreeView ItemTemplate="{DynamicResource TreeViewDataTemplate}"
ItemsSource="{Binding ElementName=UserList Path=SelectedItem.SubViewModelCollection}"/>
Assuming the items in UserList are type SubViewModel which has an IEnumerable<T> property called SubViewModelCollection.

I would recommend taking a look at Prism, particularly the portion pertaining to Event Aggregation. What this will allow you to do is publish an event in your application when an item in the TreeView is selected, consume that event elsewhere in your application and bind the ItemControl to the selected SubViewModel all without having to introduce any unnecessary coupling between these two pieces of your application.

Related

WPF ItemsControl bound to UserControls

An ItemsControl binding to a collection of UserControl objects works fine. However, I would like to apply additional XAML, such as a Border, etc.
However, instead of a Border with the UserControl, only the UserControl itself is rendered. The <ItemsControl.ItemTemplate> does not seem to have any effect.
Question: How can I design an ItemTemplate with additional XAML? Currently, this tag seems to be "ignored".
ViewModel: ObservableCollection<UserControl> MyUserControls
<ItemsControl ItemsSource="{Binding MyUserControls, lementName=popupContainer}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border ...>
<ContentControl Content="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
A look at the reference source reveals that the IsItemItsOwnContainerOverride method of the ItemsControl class has this implementation:
protected virtual bool IsItemItsOwnContainerOverride(object item)
{
return (item is UIElement);
}
So if you pass a collection of UIElements to the ItemsSource of an ItemsControl, these elements are used directly as item containers, without the usual wrapping inside a ContentPresenter. Hence no ItemTemplate is applied at all.
So the answer to the question
How can I design an ItemTemplate with additional XAML?
is: Not at all if the ItemsSource is a collection of UIElements.
You should instead follow the basic idea of the ItemsControl class, and assign a collection of data item objects to the ItemsSource property. Then select appropriate UI controls by DataTemplates that have their DataType property set to the types of the different data items.
Or you create a derived ItemsControl which overrides the IsItemItsOwnContainerOverride method:
public class MyItemsControl : ItemsControl
{
protected override bool IsItemItsOwnContainerOverride(object item)
{
return false;
}
}

Find Listbox in DataTemplate inside ItemsControl

To create my own drophandler I need to get access to the listbox which is inside an ItemsControl.
XAML
<ItemsControl ItemsSource="{Binding Days}" Name="myCalendar" Margin="200,75,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="6" Columns="7">
</UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ItemTemplate -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Date}">
</TextBlock>
<ListBox Name="Scenes" ItemsSource="{Binding Scenes}" dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True" dd:DragDrop.DropHandler="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<Run Text="{Binding Path=SlugLine}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How do I get access or find the ListBox inside the ItemsControl from my ViewModel, not via code behind?
You absolutely do not want the VM knowing about the view. The whole point of MVVM is to decouple the view and the logic.
Instead handle the drop in the code-behind. Some people seem to believe that there should be no code-behind in MVVM, but it's absolutely fine as long as it's specific to the view, and there is no VM logic in there.
Imagine that you have hooked up a completely new view to your VM, say a console based text view. If your view logic remains intact with a completely new view, since it's all in the VM then you're fine. If you have logic in the code-behind that would disappear when you changed views, then you need that logic moved down to the VM.
Drag and drop is fine. You handle the drop in code-behind and then call the VM to do the logic associated with the drop, say via a bound command. If replacing the view with a text view, the drop could be CTRL-V instead, but the same VM command would be called to do the logic associated with the drop.
As mentioned, one way to call the VM from the code-behind would be to have a dependency property on the view that gets bound to a command in the VM, with your code-behind just invoking the command via the property.
A simpler way is to just cast the DataContext to your VM type and call a function directly. A lot of people dislike this since it couples the view to a VM type, but I see no issue with it at all. The view is already coupled to all bound properties on the VM anyway. VM's should be view agnostic, but the view NEEDS to know about the VM in order to be useful.

WPF ItemsControl binding not updating when bound to an ObservableCollection in an object in the View Model

Basically I have an object in my view model that contains an ObservableCollection of a custom object. My XAML's DataContext is set to my ViewModel, my ViewModel contains a 'Scratchdisk' object, and the Scratchdisk object contains an ObservableCollection of Frame objects. Both the Scratchdisk and the Collection are set up as DependencyProperties.
In short: XAML --DataContext--> EditorViewModel --DependencyProperty--> Scratchdisk --DependencyProperty--> ObservableCollection<Frame>
The Frame object has 3 standard properties: Index, Image, and ImageUrl.
I'm trying to bind to the ObservableCollection in my XAML using this code:
<ItemsControl DataContext="{Binding Source=ThumbnailScratchdisk}" ItemsSource="{Binding Frames, UpdateSourceTrigger=PropertyChanged}" ItemTemplate="{StaticResource ThumbnailTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"></VirtualizingStackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Where ThumbnailTemplate is defined in Window Resources as:
<DataTemplate x:Key="ThumbnailTemplate">
<Image Width="128" Height="96" Source="{Binding ImageUrl}"/>
</DataTemplate>
Theoretically, what should happen is, the Scratchdisk should receive filenames, create Frame objects, add them to the Collection, and then the binding should display them. The ObservableCollection is working and being populated, but the binding doesn't seen to be updating. All the updatable properties are set as DependencyProperties so the binding should update shouldn't it?
Links to the files:
XAML
ViewModel
Scratchdisk
Frame
The problem is in the binding of the DataContext of your ItemsControl.
You're setting it to "{Binding Source=ThumbnailScratchdisk}", but what you (presumably) want is to set it to just "{Binding ThumbnailScratchdisk}".
The DataContext of the page is already an instance of EditorViewModel, and you want the DataContext for the ItemsControl to bind to the property ThumbnailScratchdisk of that viewmodel.
Trying changing the binding path in XAML to ThumbnailScratchdisk.Frames

ListView default selected items mvvm

I have ListView with dependency property for selecting more than one item. ListView sample code is here:
<ListView ItemsSource="{Binding Test}"
ItemTemplate="{StaticResource SomeTemplate}"
SelectionMode="Multiple"
multipleBind:SelectionChangedCommand.Command="{Binding SelectionChangedCommand}">
</ListView>
It works properly but what I want is pre-select default items and I don't know How do it. Is it a possible? Please give me some hit.
Thanks

Adding custom control to listbox

I have a custom control ListItem. I need to display five such items in a window and these items could change during runtime; items could be added or deleted, or content could change in ListItem.
ListBox appears to be a good solution to display items. But what I have seen is we can add items and style them, and can handle updates with data trigger.
myListBox.Items.Add(new { FileName = "SomeFile", State="Uploaded" });
But we can not do something like
ListItem curItem = new ListItem();
myListBox.Items.Add(new { curItem });
Even if I do it shows empty item in the list.
So if I want to add my custom control to some listbox, how could that be possible. That is using ListBox just as a container so we can get away from the pain of positioning and all that after list changes. Or is there a better way to do that?
You are in luck - this is the bread and butter of WPF! Set the ItemsSource of your ListBox (possible in XAML or cs):
myListBox.ItemsSource = myEnumerableCollection;
or
<ListBox ItemsSource="{Binding MyItemsProperty}">
Use a DataTemplate (you do not need a UserControl) to style each item in XAML:
<ListBox ItemsSource="{Binding MyItemsProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FileName}"/>
<TextBlock Text="{Binding State}"/>
<!--Whatever you want-->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If your collection is an ObservableCollection<T> changes to that collection (e.g. items added or removed) will be reflected in the ListBox automatically. If T implements INotifyPropertyChanged changes to properties on each item will also automatically show up on the UI.
For more see the WPF Binding Overview.
Don't create or manipulate UI elements in procedural code in WPF.
<ListBox ItemsSource="{Binding SomeCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<my:MyControl/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where my:MyControl is a UserControl with whatever UI you want.

Categories

Resources