How to bind StackPanel Children from a ViewModel? - silverlight

New to Silverlight. I'm working on a chat application where new chat messages are added to the bottom of a list. I had a working version that used as StackPanel inside a ScrollViewer and then in some code behind used StackPanel.Children.Add().
I'm trying to convert this to a View-ViewModel approach, and I can't figure out how to bind the Children of the StackPanel to any collection property. I've tried this:
<ScrollViewer Name="scrollMessages" Grid.Row="2" Margin="0,0,0,0" VerticalScrollBarVisibility="Visible">
<StackPanel x:Name="pnlMessages" Orientation="Vertical" Children="{Binding Path=ExampleTBs}" />
</ScrollViewer>
where ExampleTBs is a collection of TextBlocks created in code. This fails XAML parsing, the Children property isn't bindable in this way.
Is the approach of binding to the StackPanel itself fixable? Should I be using a different container type? I saw another question where the guy created the entire StackPanel in code and then used a ContentPresenter...
Bottom line, I'd like to find a way to databind my view to a viewmodel using something like a StackPanel as a container where successive items will be added to the container over time. Best approach?

Use a ListBox (or any other ItemsControl) and bind the ItemsSource property to an ObservableCollection in your ViewModel.

Do you need to use a StackPanel? If you use an ItemsControl instead, this still presents each chat message in a vertical list, and also allows for binding of the data.

Related

Loading more items when scrollbar scrolled to the end in ListView(WPF)

I want to implement a function that using a ListView to load items, but the number of items are very large, so I want when the user scroll the scrollbar to the end of the ListView, it auto load more items. I have found a solution to detect if the scroll is scrolled to the end here: Detect when WPF listview scrollbar is at the bottom? But in MVVM, I didn't find a solution to pass EventArgs. Is there any other solutions?
My Xaml looks like this:
<ScrollViewer>
<ListView>
...
</ListView>
</ScrollViewer>
Thanks!
You could have your View execute an ICommand Property on your ViewModel and take advantage of the CommandParameter parameter of the Execute method. However, I would warn that passing the state of your View to the ViewModel so that the ViewModel can determine which items to load is not an appropriate MVVM pattern. Generally, the ViewModel needs to drive the show, even if that includes offloading some UI state information from the View to the ViewModel so that it can natively deduce what to load.
If you use MVVMLight in your WPF project, just set PassEventArgsToCommand true.
eg:
xmlns:ni="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mv="http://www.galasoft.ch/mvvmlight"
<ni:Interaction.Triggers>
<ni:EventTrigger EventName="SelectionChanged">
<mv:EventToCommand Command="{Binding YourCommand}" PassEventArgsToCommand="True" />
</ni:EventTrigger>

How to reuse Menu and Toolbar in different WPF UserControls with adding new buttons?

I'm trying to port current WinForms app to WPF and need help with building WPF class structure.
Currently in WinForms, I have one base class (with Menu, Toolbar, Grid and Context menu) and several inherited classes with different datasources and columns for the grid and additional buttons for menu, toolbar and context menu.
Questions:
What will be the best WPF class structure for my usercontrol to avoid inheritance?
Can I move ToolBar into ResourceDictionary (e.g. ControlTemplate)?
How to add new buttons or to bind commands to existed buttons, if buttons will be specified in a separate resource file?
Any other ideas?
Due to inheritance restrictions in WPF I see only one way to avoid duplicating Menu, Toolbar, etc. - implement base class in C# code only without XAML. And likely I can't use XAML for inherited classes as well (have no idea how to add new buttons in XAML into Toolbar created in base class)
In WPF a Toolbar is an ItemsControl (same for Menu), so it has an ItemsSource property you can bind to your collection of toolbar items.
<Window.Resources>
<DataTemplate x:Key="ItemTemplate1">
<StackPanel>
<TextBlock Text="{Binding Property1}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding MyViewModel}">
<ToolBar HorizontalAlignment="Left" VerticalAlignment="Top" ItemTemplate="{DynamicResource ItemTemplate1}" ItemsSource="{Binding ToolbarItems}"/>
</Grid>
In this code, ToolbarItems is an ObservableCollection< MyToolBarItem >, where MyToolbarItem is a viewmodel that represents one toolbar item.
MyToolBarItem could be the base class, with several classes inheriting from it. Then you can use a DataTemplateSelector to use a different template depending on the type of toolbar item it is.
In this way, all your user controls that have a toolbar can use the same templates you define in your dictionary; each toolbar is just bound to a different collection of MyToolBarItems.
If some of that sounds overwhelming, you can read up on some MVVM. It is the design pattern that makes WPF great.

Grid with content from Binding

I have a library that generates a Grid based on input parameters. The Grid may contain different controls based on the input. I want to create a ListBox where each list item will get its own generated Grid. Is this doable? I couldnt find any Panel-derived (Gird, StackPanel etc) that expose the Content property like Button for example.
How are you passing the data to build the Grid?
I'll assume you have a control that receives the data via a Dependency Property. I.e. you have something like MyControl.MyData property, where MyData is a Dependency Property. In that case, try using a DataTemplate.
Make an ObservableCollection where each item is the data you need to pass in order to build the grid.
On the instance of the ListBox, define ItemTemplate to use a DataTemplate consisting of your control.
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<myAssembly:MyControl MyData="{Binding }"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
#XAMeLi is almost right on the money with his answer - what would be better is a DataTemplateSelector, that way each data item can have a different template (or generated grid). In your class that extends DataTemplateSelector you can easily generate or load the appropriate grid layout as either a separate control or as a dynamic DataTemplate.
Check this article for a good example: WPF Tutorial - How To Use A DataTemplateSelector

How do you navigate a complex Visual Tree in order to re-bind an existing element?

In the above image, child is a ContentPresenter. Its Content is a ViewModel. However, its ContentTemplate is null.
In my XAML, I have a TabControl with the following structure:
<local:SuperTabControlEx DataContext="{Binding WorkSpaceListViewModel}"
x:Name="superTabControl1" CloseButtonVisibility="Visible" TabStyle="OneNote2007" ClipToBounds="False" ContentInnerBorderBrush="Red" FontSize="24" >
<local:SuperTabControlEx.ItemsSource>
<Binding Path="WorkSpaceViewModels" />
</local:SuperTabControlEx.ItemsSource>
<TabControl.Template>
<ControlTemplate
TargetType="TabControl">
<DockPanel>
<TabPanel
DockPanel.Dock="Top"
IsItemsHost="True" />
<Grid
DockPanel.Dock="Bottom"
x:Name="PART_ItemsHolder" />
</DockPanel>
<!-- no content presenter -->
</ControlTemplate>
</TabControl.Template>
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:WorkSpaceViewModel}">
....
WorkSpaceViewModels is an ObservableCollection of WorkSpaceViewModel. This code uses the code and technique from Keeping the WPF Tab Control from destroying its children.
The correct DataTemplate - shown above in the TabControl.Resource - appears to be rendering my ViewModel for two Tabs.
However, my basic question is, how is my view getting hooked up to my WorkSpaceViewModel, yet, the ContentTemplate on the ContentPresenter is null? My requirement is to access a visual component from the ViewModel because a setting for the view is becoming unbound from its property in the ViewModel upon certain user actions, and I need to rebind it.
The DataTemplate is "implicitly" defined. The ContentPresenter will first use it's ContentTemplate/Selector, if any is defined. If not, then it will search for a DataTemplate resource without an explicit x:Key and whose DataType matches the type of it's Content.
This is discussed here and here.
The View Model shouldn't really know about it's associated View. It sounds like there is something wrong with your Bindings, as in general you should not have to "rebind" them. Either way, an attached behavior would be a good way to accomplish that.
I think the full answer to this question entails DrWPF's full series ItemsControl: A to Z. However, I believe the gist lies in where the visual elements get stored when a DataTemplate is "inflated" to display the data item it has been linked to by the framework.
In the section Introduction to Control Templates of "ItemsControl: 'L' is for Lookless", DrWPF explains that "We’ve already learned that a DataTemplate is used to declare the visual representation of a data item that appears within an application’s logical tree. In ‘P’ is for Panel, we learned that an ItemsPanelTemplate is used to declare the items host used within an ItemsControl."
For my issue, I still have not successfully navigated the visual tree in order to get a reference to my splitter item. This is my best attempt so far:
// w1 is a Window
SuperTabControlEx stc = w1.FindName("superTabControl1") as SuperTabControlEx;
//SuperTabItem sti = (SuperTabItem)(stc.ItemContainerGenerator.ContainerFromItem(stc.Items.CurrentItem));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(stc);
//ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(sti);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
The above code is an attempt to implement the techniques shown on the msdn web site. However, when I apply it to my code, everything looks good, except myDataTemplate comes back null. As you can see, I attempted the same technique on SuperTabControlEx and SuperTabItem, derived from TabControl and TabItem, respectively. As described in my original post, and evident in the XAML snippet, the SuperTabControlEx also implements code from Keeping the WPF Tab Control from destroying its children.
At this point, perhaps more than anything else, I think this is an exercise in navigating the Visual Tree. I am going to modify the title of the question to reflect my new conceptions of the issue.

Creating instances of resources?

I'm brand spanking new to WPF and am trying to play around with projects to better understand what I'm reading.
My understanding of a resource is that it is the instance, you can't use it like a factory and create instances of it. For example, a XAML-defined rectangle. You can reference it, but you can't have numerous instances of it all over the surface.
In WPF, what would be the way to do that? If I define a Rectangle as a resource with specific properties and wanted to have multiple instances of that within a dynamically-generated grid, how should I be going about it? Or is there a different way I should be trying to do this?
Purely academic exercise with no real-world application.
Actually there's nothing about resources in particular that prevents you from using it multiple times. A perfect example of this is brush resources, style resources, etc. You define them in XAML and the XAML parser creates a single instance of the resources and stores them in the resource dictionary and these brushes, styles, etc can be used as property values many times even though only a single instance of the resource was created.
But having said that, as you noted, you can't really define a Rectangle resource and use it multiple times in the visual tree. This has nothing to do with the fact that it's a resource, but rather it has to do with the fact that a FrameworkElement cannot be a child of more than one parent element.
So what we have instead is called "templates". These tell WPF how to create an element tree but does not actually create the tree until you instantiate the template. Below is an example.
<UserControl>
<ItemsControl ItemsSource="{Binding WholeBunchOfItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Fill="Yellow" />
<ContentPresenter Content="{Binding}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
In this example I've bound an ItemsControl to a collection of some sort. For each item in the collection, the ItemsControl will use my DataTemplate to render the item. Within a DataTemplate you can use data binding to access the current item.
I would suggest reading up on MSDN about ControlTemplate, DataTemplate, and Style. These are all important concepts in WPF/Silverlight.
To get multiple instances replicated across a grid or listbox, you need to set the data template to define the UI controls for each row of data, and then databind the grid or listbox to a collection of data that determines how many rows and the individual field values.
Key term for you to research first: data template.

Resources