Dynamically adding controls to View from Controller - wpf

In WPF, I am using the MVVM model.
I have a View with a UniformGrid and a ViewModel where I would like to add items to the UniformGrid how would I accomplish this without doing it in the code behind?

The UniformGrid is a Panel; it's intent is not what you are trying to accomplish. You can achieve what you are trying to do however by adjusting the default ItemsPanelTemplate within an ItemsControl.
<ItemsControl ItemsSource="{Binding PropertyNameOnViewModel}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
This will allow you to bind to your ViewModel property within the ItemsControl and the data will get represented visually within a UniformGrid.

Related

How do I bind a 1D array to a 2D display (datagrid or gird) in WPF?

I'm trying to set up databinding of a usercontrol. I have an item source List, its length is 64, I want to display it in 8x8 grids instead of 1x64 list because there is not enough room to display in UI. How to do that in WPF?
If they should all be the same size, then I would recommend using a UniformGrid. Here's an example of how you would use a UniformGrid in an ItemsControl:
<ItemsControl ItemsSource="...">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="8" Columns="8" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
You should be able to substitute ItemsControl for any of its descendants, such as ListBox.
If UniformGrid isn't exactly what you're looking for, my second suggestion would be WrapPanel. That should get you roughly the layout you want, but it wouldn't be a fixed 8x8 grid.
If neither of those panels work for you, I think the only remaining option would be to build your own.

WPF Dashboard and Widgets

Hi I would like to write some kind of dashboard. You should be able to drag widgets on the dashboard from some source. The layout of the widgets should be free (first Canvas, later some own Panel).
My questions:
Do you have any hints for information about this kind of controls for me?
Is it a good idea to use Selector as BaseClass or should inherit from Control
I would make a BaseClass for all widgets, and then build a ViewModel that inherits from that BaseClass for each Widget, along with a View to go with that ViewModel
After that, I would have something like ObservableCollection<WidgetBaseViewModel> OpenWidgets in the main application ViewModel, and bind it to an ItemsControl.
The ItemsControl would have it's ItemsPanelTemplate set to a Canvas, and each WidgetBaseViewModel would contain a Top, Left, Height, and Width property.
The actual UI to display each Widget with would be based on a DataTemplate, and could be anything you want, although a UserControl would be easiest
<ItemsControl ItemsSource="{Binding OpenWidgets}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:WidgetAViewModel}">
<local:WidgetAView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:WidgetBViewModel}">
<local:WidgetBView />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas ... />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Also, you'll need to bind your Canvas.Top/Canvas.Left on the ItemContainerStyle instead of on the actual ItemTemplate to get it to display correctly in the canvas.

How to populate collection controls in a WPF custom control?

I'm learning the ins and outs of creating custom controls in WPF. The concepts I understand thus far:
The code defines the control's behavior.
The template defines the control's look and feel, the visual aspect.
You can bind elements of the template to properties of the underlying control object.
If the template has multiple collection controls, such as StackPanels, how do you bind them so that they are populated by underlying collections? My first inclination was to make use of the DataContext of each StackPanel, but I couldn't make it work. I get the feeling I'm missing a key concept that would solve this for me.
What did you want to be in these StackPanels? Panels are used for arranging items. If you want to show a collection of items, you probably want to be using an ItemsControl (or one of the many types of ItemsControls). An ItemsControl is very powerful- you can specify how the items are displayed and how the panel that they're displayed on is displayed as well. You can even specify that the panel is a StackPanel. For example,
<ItemsControl ItemsSource="{Binding ElementName=root, Path=List1}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!-- Template defines the panel -->
<StackPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Template defines each item -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then you wanted to bind to a list of items, which is very easy with an ItemsControl! In your case with the custom control, you may want to expose dependency properties on the control itself in the code-behind and then bind to those from the XAML. For example, you can create dependency properties for the various lists you have like this:
public static readonly DependencyProperty List1Property = DependencyProperty.Register(
"List1",
typeof(IList<string>),
typeof(MyControl));
public static readonly DependencyProperty List2Property = DependencyProperty.Register(
"List2",
typeof(IList<string>),
typeof(MyControl));
Then you can bind the ItemsSource property of your ItemsControls:
<ItemsControl ItemsPanel="..." ItemsSource="{Binding ElementName=root, Path=List1}" />
<ItemsControl ItemsPanel="..." ItemsSource="{Binding ElementName=root, Path=List2}" />
(in this case I assume that the custom control has a x:Name="root")
I hope this helps!
You can't bind the contents of a StackPanel directly. It's simply a layout control.
What you could do, however, is use a ListBox and set its ItemsPanel to be a StackPanel (or whatever other layout control you need). Then you can set the ListBox's ItemsSource to whatever underlying collection you want.

How can I make a ListBoxItem stretch Vertically

I would like to make a ListBox function like a Grid. Each time a new item is added it should look like a a new GridRow was added (with a height of star). So if there are two items they will each take up half of the available space. At some point the Grid row will be smaller than the items MinHeight at which point the Grid will expand and a containing ScrollViewer can kick in.
You will see this behavior with a grid inside a ScrollViewer. However, I need to get this working with a ListBox so I can just set the ItemsSource, create a DataTemplate and move on.
The problem with the default ListBox ItemsPanel is that it will not let my first item expand to fill all the available space.
UPDATE:
Here's the code to get it working:
<ListBox VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" Width="Auto" Height="Auto">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"></UniformGrid>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
This SO post has some pretty good information that seems relevant to your post
WPF - Why Listbox items do not fill uniformgrid

WPF WrapPanel/StackPanel with DataTemplate?

What must I do to use a DataTemplate in a WrapPanel or StackPanel?
On a ListBox its so easy but i cant find a way to do it on a Panel...
Edit: What i want is a ListBox that places the items like a WrapPanel.
If I understand you correctly, you can use the ItemsPanel property of your container. I used something similar to make my ItemsControl layout using a horizontal StackPanel:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
So, more specifically for your case:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
There isn't a DataTemplate property for those panels..
You can however use a ControlTemplate for these panels, and set these in the Template property of these panels...
HTH

Resources