Bind Collection of User Control's to WPF Grid [duplicate] - c#

I am using a Grid as ItemsPanel for a list dynamically bound to an ItemsControl. The code below is working - with a remaining problem: I can’t find a way to dynamically initialize the ColumnDefinitions and RowDefinitions of the grid. As consequence all values are placed on top of each other.
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}"/>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Please be aware, that I am searching an answer according the MVVM pattern. Therefore sub classing and code behind are just workarounds, but no solutions.

You'll need some way to tell the Grid how many Rows/Columns it has. Perhaps as each Item loads you could check the value of RowIndex and ColumnIndex and add Rows/Columns to the Grid if needed.
As another alternative, perhaps you can expose RowCount and ColumnCount properties in your ViewModel that return the max RowIndex and ColumnIndex, and in the Grid's Loaded event add however many Columns/Rows you need.
I find it perfectly acceptable to use code-behind in MVVM IF the code is related to the UI only.
Another idea would be to arrange your items in your code-behind into a 2D grid before returning it to the View, and then bind that Grid to a DataGrid with AutoGenerateColumns=True and the headers removed
Update
My current solution to this problem is to use a set of AttachedProperties for a Grid that allow you to bind RowCount and ColumnCount properties to a property on the ViewModel
You can find the code for my version of the attached properties on my blog here, and they can be used like this:
<ItemsPanelTemplate>
<Grid local:GridHelpers.RowCount="{Binding RowCount}"
local:GridHelpers.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>

Your grid has zero rows and columns so everything will be displayed on top of each other. Do something like below and it will work.
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

i found this from here The ItemsControl
<ItemsControl ItemsSource="{Binding VehicleMakes}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="300" Height="100" Click="ButtonOption_Click" Tag="{Binding Name}">
<StackPanel Orientation="Vertical">
<Image
Initialized="Image_Initialized"
Tag="{Binding ResourseImageName}"
Width="116"
Height="30"
Margin="0,0,0,10" >
</Image>
<TextBlock Text="{Binding Name}" VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Related

WPF: DataTemplate binding for different usercontrol

I'm new on WPF and I'm tryng to figure out how to implement a binding with different type of user control.
After the user clicks a button, a usercontrol (a simple shape like rectangle or ellipse) is added to the window.
I'm tryng to use an MVVM approach so the xaml appears as follow:
...
Title="{Binding Path=Titolo}"
Height="450" Width="800"
d:DataContext="{d:DesignInstance vm:MainWindowViewModel}">
<Canvas>
<Button Content="Add Shape" Command="{Binding TestCommand}"/>
<ItemsControl ItemsSource="{Binding RectCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomRectangle/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
All works fine but I want to add different type of UserControl (not only MyCustomRectangle) using the same button (for example, randomly adding rectangle or ellipse).
A possible solution could be duplicate the section of ItemsControl and select a different collection of binding:
<Canvas>
<Button Content="Add Shape" Command="{Binding TestCommand}"/>
<ItemsControl ItemsSource="{Binding RectCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomRectangle/> <!-- bind to my usercontrol -->
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
<ItemsControl ItemsSource="{Binding EllipseCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomEllipse/> <!-- bind to my usercontrol -->
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
I don't think this is the correct solution, especially because I would to add many different types of shapes (and also text and images).
So, if exist, what is the correct way to bind a data template to different type of usercontrol?
Is MVVM the correct approach to solve this problem?
What you can do is to bind to a global list with all Shapes.
And then you can define different DataTemplates for different Types.
Like this:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type MyType1}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type MyType2}">
....
</DataTemplate>
</ItemsControl.Resources>

How to add ItemsSource to Combobox nested in ListBox Template from codebehind?

I have some problem with ComboBox nested in ListBox. I want to add the same ItemsSource(gained from database, adding from codebehind) to each of comboboxes created in ListBox, but don't know how. Any ideas how to do this?
</Window.Resources>
<Style x:Key="lbxKey" TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type Type1}">
<StackPanel Orientation="Horizontal" Width="Auto" HorizontalAlignment="Stretch">
<TextBlock TextTrimming="CharacterEllipsis" Width="200" Text="{Binding NAMETYPE1}" HorizontalAlignment="Left"></TextBlock>
<ComboBox HorizontalAlignment="Stretch" Tag="{Binding IDTYPE1}">
<ComboBox.ItemsSource>
<!-- no idea what should be here or even if this is needed -->
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type Type2}">
<StackPanel Orientation="Horizontal" Width="100">
<TextBlock Text="{Binding NAMETYPE2}" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
In codebehind:
lbxListbox.ItemsSource = observableCollectionFromDatabase;
//here should be sth to set ItemsSource for comboboxes in ListBox
There should be a collection type property in your item class (anything that implements IEnumerable will do). Then you would bind the ComboBox's ItemSource like this:
<ComboBox Tag="{Binding IDTYPE1}" ItemsSource="{Binding ITEMS}" ...>

ItemsControl with none background WPF

I would like to ask you, if is possible to have ItemsControl without background (x:null), not transparent.
I have collection with data, and these are showed in ItemsControl with help of DataTemplate. Some data in datatemplate are collapsed, and I need to be able to clickable on another control behind the itemscontrol.
Here is example what I mean:
<Button x:Name="bt_behind"></Button>
<ItemsControl ItemsSource="{Binding ListOfData}" Background="{x:Null}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="{x:Null}"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Class:Data}">
<Grid Width="100" Background="{x:Null}">
<Rectangle x:Name="rec" Fill="Red" Height="100" Visibility="Collapsed">
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<Setter TargeName="rec" Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Example where item3 is partly collapsed and marked area, where is empty place
I set everywhere the background to null (try itemcontainerstyle too), but without success. On button behind ItemsControl still is not clickable. I think that ItemsControl have transparent background for events, but is it possible to remove this background?
Thanks for any advice and sorry for my english:)
-pav-
Well, as i said, it's all working. Fixed XAML:
<Grid>
<Button x:Name="bt_behind" Content="behind" Click="Bt_behind_OnClick"/>
<ItemsControl ItemsSource="{Binding ListOfData}" Background="{x:Null}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Background="{x:Null}"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Data}">
<Grid Width="100" Background="{x:Null}">
<Rectangle x:Name="rec" Fill="Red" Height="100" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="true">
<Setter TargetName="rec" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Just for test.
private void Bt_behind_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("");
}

Is it possible to use an existing grid when creating an ItemsControl?

I'm trying to create a layout where both the number of rows and the number of columns are dynamic, like shown below:
I am looking to have all of the controls hosted in one grid so that I can get tabbing working from top to bottom, left to right. The code which I am trying to get working is shown below:
<Grid base:GridHelpers.RowCount="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dx:DXWindow}, Path=Groups.Count}" Name="EnclosingGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--Labels-->
<ItemsControl ItemsSource="{Binding Rows}" ItemsPanel="{Binding ElementName=EnclosingGrid}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Grid.Column="0" Content="{Binding Path=FieldName}" VerticalContentAlignment="Center" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--Process fields-->
<ItemsControl ItemsSource="{Binding Cells}" ItemsPanel="{Binding ElementName=EnclosingGrid}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding ParentRow.RowIndex}" />
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I've tried setting the ItemsPanel attribute to the enclosing grid, but unfortunately that doesn't work.
Is the only way really to set up the grid as a ItemsPanelTemplate inside the ItemsControl?
Are there any other approaches? Or would I have to roll my own ItemsControl?
ItemsPanel property type is ItemsPanelTemplate so you can't bind this property directly to some existing control.
You can think about this property as a kind of factory class, which is supposed to provide container control for items.
This is what MSDN literally says about this type: Specifies the panel that the ItemsPresenter creates for the layout of the items of an ItemsControl.

Strange positioning of ZoomedOutView in SemanticZoom in Metro-style WinRT XAML Apps

I've been using a SemanticZoom containing a GridView in both the ZommedInView and the ZoomedOutView. The one in the ZoomedOutView doesn't seem to snap to the container though. The container is a StackPanel, located in a ScrollViewer. This means that when I zoom out, my items are offscreen if the Horizontal-alignment is set to left.
Also, when I select the zommed-in gridview in the designer, it is clearly shown in the expected place, filling the scrollviewer. The zoomed-out gridview is shown to exceed the boundaries of the page.
Swapping the content of the zoomed-in and zoomed-out views swaps the problem to the other gridview, so we can deduce the problem is not in a difference of the gridview.
Is there a way to fix it or is it a bug in the control?
<ScrollViewer
x:Name="itemGridScrollViewer"
AutomationProperties.AutomationId="ItemGridScrollViewer"
Grid.Row="1"
Margin="0,-3,0,0"
Style="{StaticResource HorizontalScrollViewerStyle}">
<!--
ItemTemplateSelector="{StaticResource hubItemTemplateSelector}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
-->
<StackPanel
Margin="116,0,40,46">
<SemanticZoom ManipulationMode="RotateInertia">
<SemanticZoom.ZoomedInView>
<GridView
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplateSelector="{StaticResource hubItemTemplateSelector}"
SelectionMode="None"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"
IsSwipeEnabled="True">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<Button
AutomationProperties.Name="Group Title"
Content="{Binding Path=Title}"
Click="Header_Click"
Style="{StaticResource TextButtonStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid MaximumRowsOrColumns="2" ItemHeight="250" ItemWidth="250" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<GridView
AutomationProperties.AutomationId="CategoryGridView"
AutomationProperties.Name="Grouped Items Zoomed Out"
ItemsSource="{Binding Path=ItemGroups}"
>
<GridView.ItemTemplate>
<DataTemplate>
<Button
AutomationProperties.Name="Group Title"
Content="{Binding Title}"
Click="Header_Click"
Style="{StaticResource TextButtonStyle}"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid ItemWidth="250" ItemHeight="250" MaximumRowsOrColumns="2" VerticalChildrenAlignment="Center" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="10" />
<Setter Property="Background" Value="#FF25A1DB" />
<Setter Property="BorderThickness" Value="1" />
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</StackPanel>
</ScrollViewer>
The solution seems to be to set the HorizontalAlignment attribute on the StackPanel that contains the SemanticZoom. If I set it to Left, the content aligns appropriately.

Categories