I have a WrapPanel that I'm filling in my ViewModel with UserControls:
<WrapPanel Width="250" Orientation="Horizontal" Margin="3">
<ItemsControl ItemsSource="{Binding PlaceableObjectsContent}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:PlaceableObjectViewModel}">
<local:PlaceableObjectUserControl>
<local:PlaceableObjectUserControl.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{Binding DataContext.OnPlaceableObjectClicked, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
</local:PlaceableObjectUserControl.InputBindings>
</local:PlaceableObjectUserControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</WrapPanel>
When I fill them manuell with random Controls, everything works fine! I already read something about problems because of Using ItemTemplate!?
If its true, how can I manage that?
Thanks
You're putting a single ItemsControl inside a WrapPanel. That won't do anything. If you want your ItemsControl to use a WrapPanel to host its own items, here's how:
<ItemsControl
ItemsSource="{Binding PlaceableObjectsContent}"
Width="250"
Margin="3"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- etc. -->
Note that something has to constrain the width of the ItemsControl for this to work: Either it must be limited by the size of its parent, or a Grid Column, or even by setting the Width or MaxWidth properties of the ItemsControl element itself, either directly or via a Style.
Related
I'm trying to make my xaml squeeze list items (images) to fit the initial window size, but they cant be fixed size because i want to scale them up as i increase the size of the window. Something like a ViewBox would do.
I load images from 2 folders (software and hardware). Number and size of the images will wary in the runtime so i want to make items be the same size regardless of images size or number of items.
That's why i used uniform grid as a items panel template.
But, this is the result I'm getting...
The ListView loads images in their full size and expands itself to fit them in, cutting of some of the items in the process.
This is my xaml:
<Window x:Class="WPF_UI_Testing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_UI_Testing"
mc:Ignorable="d"
Title="MainWindow" Height="460" Width="640">
<Grid>
<ListView x:Name="listview1">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"
FontWeight="Bold" FontSize="18" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"></StackPanel>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid ></UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
Is there a way to prevent ListView from expanding beyond window borders when populated with content?
EDIT:
I packed the entire solution with some dummy data and images if anyone wants to have a go at this...
https://drive.google.com/file/d/1IWqxSR3kpsVdCm5Qcgn6QZbZhbYz52n2/view?usp=sharing
The UniformGrid does basically what you want. The idea to use it as item panel is also correct. The only problem that arises, aside from using item groups, is the fact that the ListView wraps its panel into a ScrollViewer, which results in items or the UniformGrid to resize differently as the ScrollViewer gives the panel no size restrictions. UniformGrid needs to be hosted in a fixed size container in order to be able to calculate its children's max sizes.
You should either use the ItemsControl
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:DetailItem}">
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or override the ControlTemplate of ListView and remove the ScrollViewer (if you need its additional features of ListView):
<ListView>
<ListView.Template>
<ControlTemplate TargetType="ListView">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<ItemsPresenter />
</Border>
</ControlTemplate>
</ListView.Template>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:DetailItem}">
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The limitations of this approach is when using groups to display the items. UniformGrid should handle GroupItem (nested items) not as expected.
As said in my previous comment, if you want to group items, you need to extend a panel of your choice to manually arrange GroupItem and it's children (nested ItemsPresenter). As you think about how to calculate sizes you may realizes that it is more complicated to calculate group item sizes with dynamic grouped item sizes.
I recommend to let go the grouping and use one of the above solutions or use grouping and embrace the ScrollViewer.
I'm having a ItemsControl with custom controls wrapped inside. What I want to achieve is to always have a TextBox control along with my custom controls always at the end of WrapPanel.
Here is a slight preview:
Having ItemsControl allows us to have controls of the same type. When TextBoxis added after ItemsControl, it will unfortunately appear on the new line. Therefore I'm forced to somehow append TextBox along with my custom controls in ItemsControl.
<ItemsControl ItemsSource="{Binding MyItems}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<!-- Make items wrap -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Custom control -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CustomControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- This should be inside items control -->
<TextBox />
I did some research and it turns out that I can use DataTemplateSelector class and ItemTemplateSelector which will allow me to choose template for different items.
This is an option of course, however it will require me to insert TextBox into my MyItems (which is ObservableCollection btw.) and I'm not sure if this is the way to go since I will have to locate TextBox and move it at the end of my ObservableCollection, each time a new item is added...
So the question is: what is the proper way to solve this? Looking for experienced developers opinion on this.
You could set the ItemsSource to a CompositeCollection:
<ItemsControl ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ItemsControl.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding MyItems}" />
</ItemsControl.Resources>
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource cvs}}" />
<!-- This should be inside items control: -->
<TextBox />
</CompositeCollection>
</ItemsControl.ItemsSource>
<!-- Make items wrap -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Custom control -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CustomControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I am doing my first WPF-Project with the MVVM-Model. I have got two views that i want to look like that:
But sadly the images do not shrink to parent size, they stay in original size:
Views.PictureList
<UserControl x:Class="BIF.Views.PictureList"
xmlns:view="clr-namespace:BIF.Views"
d:DesignHeight="120" d:DesignWidth="300">
<ListBox ItemsSource="{Binding List}" <!-- List<PictureViewModel> -->
ScrollViewer.VerticalScrollBarVisibility="Hidden"
SelectedItem="{Binding CurrentPicture, Mode=TwoWay}" SelectionMode="Single">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<view:Picture DataContext="{Binding}" />
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Views.Picture
<UserControl x:Class="BIF.Views.Picture"
d:DesignHeight="200" d:DesignWidth="200">
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="4">
<Image Source="{Binding FilePath}"/>
</Border>
</UserControl>
I have already tried to set height of view:Picture manualy by adding:
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}, Path=ActualHeight}"
But thats not working 100% correct and I also do not want to set a fix height, because I want the PictureList to be resizeable.
Any suggestions are welcome.
You have to set ScrollViewer.VerticalScrollBarVisibility to "Disabled" since the "Hidden" value gives the content infinite space in the vertical direction, that's all.
StackPanel does not limit the size of container and hence all the space is available to its child elements.
You should either set limits to your stack panel or use a better control for this case, may be a Grid.
Let me know if you need help in that.
Two litte changes and everything works wonderfull now.
ScrollViewer.VerticalScrollBarVisibility="Disabled"
No ListItem in the DataTemplate
<ListBox ItemsSource="{Binding List}" <!-- List<PictureViewModel> -->
ScrollViewer.VerticalScrollBarVisibility="Disabled"
SelectedItem="{Binding CurrentPicture, Mode=TwoWay}" SelectionMode="Single">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- No ListItem here -->
<view:Picture DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thank you guys!
I'm kind of shooting in the dark on this one and have been looking around but couldn't find much of anything related. Pretty much I am trying to create an ItemsControl on a current Window that I've got, so when the user clicks an "Add Product" button on the window, it will add a UserControl to the screen in a horizontal matter.
For starters I am using an MVVM pattern and I have a PricingViewModel which is my ViewModel for the MAIN window. I have a second view model named ComparisonViewModel, which is the ViewModel for the View of the UserControl that I would like to show everytime the user hits the "Add Product" button on the PricingView. Jumping into my code, I've got a declared ObservableCollection and my AddComparison method. The Collection is instantiated in the constructor of the VM.
public ObservableCollection<ComparisonViewModel> Products { get { return _products; } }
public void AddComparison()
{
var products = IoC.Get<ComparisonViewModel>();
Products.Add(products);
}
Next, I've got and ItemsControl in the PricingView that binds to that collection in the PricingViewModel:
<ItemsControl ItemsSource="{Binding Path=Products}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
I run it and after hitting add, it just shows the collection name. How can I actually get it to Pop up with a new Comparison User Control when the user hits Add Comparison? Thanks a ton for the help in advance!
You'll want to set the ItemTemplate so the ItemsControl knows how to render each item in the collection (by default, it is just displaying the result of calling .ToString()).
<ItemsControl ItemsSource="{Binding Path=Products}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type namespace:ComparisonViewModel}">
<!-- EXAMPLE -->
<Border BorderBrush="Black"
BorderThickness="2">
<DockPanel Orientation="Horizontal">
<TextBlock Text="{Binding ComparisonResult}"
DockPanel.Dock="Right" />
<TextBlock Text="{Binding Name}"
DockPanel.Dock="Left" />
</DockPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have found that I needed to tell the ItemsControl two things... First is what type of "thing" the ItemsControl is:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
And the second is how to display the Control:
<ItemsControl.ItemTemplate>
<DataTemplate>
<c:Widget Margin="5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
The final code looks like:
<ItemsControl ItemsSource="{Binding Path=DynamicItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<c:Widget Margin="5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You also need to add a reference to your control namespace in the window declaration stuff:
xmlns:c="clr-namespace:IHateEverything.Controls"
I have a View that is basically setup like this:
<Grid>
<ViewBox>
<Grid>
<ItemsControl ItemsSource="{Binding MyItems}"
ItemTemplate="{Binding Source={StaticResource MyItemsDataTemplate}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</ViewBox>
</Grid>
The DataTemplate used here can be reduced to this:
<DataTemplate x:Key="AreaItemDisplayDataTemplate">
<Canvas Grid.ZIndex={Binding Z}>
<Grid>
// an shape is displayed here...
</Grid>
</Canvas>
I would now expect the ZIndex to be bound to the Z property of the individual items. When i debug the code, i can also see, that the Z property getter is accessed when i would expect it (whenever i raise the propertychanged event for it, eg) so i assume the binding to work correctly.
However, the ZIndex is not working as expected. The binding to the value has no effect on the actual displayed Z Order. Where am i going wrong with this code?
The content of the DataTemplate gets wrapped in a ContentPresenter so the Canvas in the DataTemplate isn't a direct child of the ItemsPanel Grid. That is the reason the ZIndex property doesn't do anything.
Move the ZIndex Binding to the ItemContainerStyle and it should work.
<ItemsControl ItemsSource="{Binding MyItems}"
ItemTemplate="{Binding Source={StaticResource MyItemsDataTemplate}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.ZIndex" Value="{Binding Z}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>