How to crop the elements not fit to a WrapPanel - wpf

The example of the WrapPanel.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl Grid.Row="1" Style="{StaticResource DestinationItemsControlStyle}"
DataContext="{StaticResource ViewModelKey}"
ItemsSource="{Binding Stations}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="5" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{DynamicResource DestinationButtonStyle}">
<TextBlock Text="{Binding FullName}" Style="{StaticResource DestinationTextBlockStyle}"
TextTrimming="CharacterEllipsis" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
If there are to many elements then all these "excessive" elements become rendered in half of their's real size. Is there a way not to render such the elements?
Am I forced to use something like VirtualizedWrapPanel?
I also want to notice that I can't use a scroll bar. All the "excessive" elements should be rendered on the next page which can be visited by the user's click on the button "Next".

This XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl Name="ItemsControl1" Grid.Row="1"
ItemsSource="{Binding Stations}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="5" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="20" MinHeight="50" MinWidth="400">
<TextBlock Text="{Binding FullName}"
TextTrimming="CharacterEllipsis" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
and the addition of 70 items in the Stations collection at startup, produces this result:
What does your DestinationItemsControlStyle look like?
For reference, this is the code-behind that runs on Startup:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModelKey vmk = new ViewModelKey();
ItemsControl1.DataContext = vmk;
}
}
and in the ViewModelKey class:
public class ViewModelKey : INotifyPropertyChanged
{
public ObservableCollection<station> Stations { get; set; }
public ViewModelKey()
{
Stations = new ObservableCollection<station>();
for (int i = 1; i < 70; i++)
{
Stations.Add(new station("This is station " + i.ToString()));
}
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

Related

Performance Issue with ListControl and Observable Dictionary WPF

I have an itemsControl bound to an observable dictionary, showing both the Key and Value in two textboxes,
It takes about 15 seconds for the usercontrol to load.
I tried virtualizing stackpanels and switching it to either a listbox or using a regular dictionary, and the lag still occurs
Any ideas what might be causing this, and how I can get it to load faster?
public partial class WordsView : UserControl, INotifyPropertyChanged
{
public WordsView()
{
InitializeComponent();
Dictionarylist.ItemsSource = curDic;
}
private ObservableDictionary<string,int> cur_dic = new ObservableDictionary<string, int>(App.PredDic);
public ObservableDictionary<string, int> curDic
{
get { return cur_dic; }
set
{
SetField(ref cur_dic, value, "curDic");
}
}
}
}
and my xaml
<UserControl x:Class="Ez.Management.WordsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ez.Management"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
xmlns:properties="clr-namespace:Ez.Properties"
xmlns:main ="clr-namespace:Ez"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="1" Header="Words">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl x:Name="Dictionarylist" ItemsSource="{Binding curDic}" VirtualizingStackPanel.IsVirtualizing="True"
ScrollViewer.CanContentScroll="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel></VirtualizingStackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Key}" Grid.Column="0" />
<TextBlock Text="{Binding Value}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</GroupBox>
</Grid>
</StackPanel>
</UserControl>
ScrollViewer.CanContentScroll is taking time... Try setting it to False. Like,
ScrollViewer.CanContentScroll="False"

ItemsControl fill remainder of columns

We have a page which we use to display a lot of information at once.
On this page we want a readouts section in the top left, and then for the remainder of that columns, and for the rest of the page we want to display some sensor outputs. Layout is roughly the following.
What I have attemtped so far is as follows
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<controls:ParameterDisplayPanel HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Columns="1"
Grid.Column="0"/>
<ItemsControl
Grid.Column="0"
Grid.ColumnSpan="3"
ItemsSource="{Binding ItemCollection, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:SensorControlView
DataContext="{Binding}"
Width="280"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
This returns a layout where the sensors overlap the readout panel.
If instead of using a grid I use a wrap panel, I get a row at the top which displays the readouts, and then all the sensor states start beneath that first row.
You could try sth. like this.
Note that this is not done yet. It's just a raw example.
Basically what I did is I added the SensorReadoutControl first and then added the remaining SensorItems to the same collection.
I then used a WrapPanel.
This is what it looks like (added some BackgroundColors to make more clear what control is where !)
So here is my example code:
XAML:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="SensorItemTemplate">
<Grid Background="Gray" Height="50">
<Border BorderThickness="1" BorderBrush="Pink"
Margin="5">
<TextBlock Text="{Binding Name}"
Width="200"
TextAlignment="Center"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Border>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Items}"
Background="Green"
ItemTemplate="{StaticResource SensorItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
ViewModel:
public class ViewModel
{
public ViewModel()
{
Grid grid = new Grid() { Width = 200, Height = 150, Background = Brushes.Blue };
grid.Children.Add(new TextBlock()
{
Text = "Sensor readouts" ,
VerticalAlignment = System.Windows.VerticalAlignment.Center,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
});
Items.Add(grid);
for (int i = 0; i < 50; i ++)
{
Items.Add(new SensorItem() { Name = $"SensorItem {i}" });
}
}
public List<object> Items { get; set; } = new List<object>();
}
public class SensorItem
{
public string ItemTypeName { get; set; } = "SensorItem";
public string Name { get; set; }
}
I guess that won't satisfy your needs but it may help you to progress.

Create multiple visuals per generated item

Here's the plan. I have an ItemsControl that will have a two columns Grid as its main panel:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
For each item in the underlying VM, I need to generate a Label that will go into first column, and a Rectangle that will go into the second one. How can I achieve this?
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:TimeSlotVM">
???
</DataTemplate>
</ItemsControl.ItemTemplate>
As you want to use Grid.Row attached property, then assign this attached property to the parent ContentPresenter by the GridHelper.
And the visual tree will be like that:
Grid
-ContentPresenter Grid.Row="1"
-Gird
-ContentPresenter Grid.Row="0"
-Gird
-ContentPresenter Grid.Row="2"
-Gird
...
GridHelper.cs
public class GridHelper
{
public static readonly DependencyProperty GridRowProperty =
DependencyProperty.RegisterAttached("GridRow", typeof(bool), typeof(GridHelper),
new PropertyMetadata(false, new PropertyChangedCallback(OnGridRowPropertyChanged)));
public static bool GetGridRow(DependencyObject d)
{
return (bool)d.GetValue(GridRowProperty);
}
public static void SetGridRow(DependencyObject d, bool value)
{
d.SetValue(GridRowProperty, value);
}
private static void OnGridRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Grid grid = d as Grid;
bool gridRow = (bool)e.NewValue;
if (gridRow)
{
// construct the required row definitions
grid.LayoutUpdated += (s, e2) =>
{
// iterate over any new content presenters (i.e. instances of our DataTemplate)
// that have been added to the grid
var presenters = grid.Children.OfType<ContentPresenter>().ToList();
foreach (var presenter in presenters)
{
// the child of each DataTemplate
var itemGrid = VisualTreeHelper.GetChild(presenter, 0) as Grid;
if (itemGrid != null)
{
Grid.SetRow(presenter, Grid.GetRow(itemGrid));
if (Grid.GetRow(itemGrid) > grid.RowDefinitions.Count - 1)
{
presenter.Visibility = Visibility.Collapsed;
}
}
}
};
}
}
}
XAML
<ItemsControl ItemsSource="{Binding ItemList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Name="outer" local:GridHelper.GridRow="True">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
...
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Grid.Row="{Binding Row}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Field}" />
<Rectangle Grid.Row="{Binding Row}" Grid.Column="1" Fill="Blue" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What about using a UniformGrid and a Collection of 24 Empty TimeSlotVM's and then insert your TimeSlotVM at the desired postion in the Collection like this :
in your VM :
TimeSlotCollection=new ObservableCollection<TimeSlotVM>(new TimeSlotVM[24]);
TimeSlotCollection.Insert(10,new TimeSlotVM());
TimeSlotCollection.Insert(5, new TimeSlotVM());
and in your View :
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type local:TimeSlotVM}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding BindingField}" />
<Rectangle Grid.Column="1" Fill="Blue" />
</Grid>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemsSource="{Binding TimeSlotCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="24"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>

Silverlight Listbox scrolling only works if i set the height

i have a listbox and i am trying to make the scrolling work without having to set the height, is this possible? Thanks. Below is the code. The scrolling does not work.
<ListBox Name="EmployeeListBox" Background="Transparent"
SelectionMode="Single"
ItemsSource="{Binding Employees, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold" Text="Name:" />
<TextBlock x:Name="TextBlock1" Grid.Column="1" Grid.Row="0"
Text="{Binding Name}" />
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and viewmodel:
public class EmployeeDataContext
{
public List<Employee> Employees { get; set; }
public EmployeeDataContext()
{
GetEmployeeList();
}
private void GetEmployeeList()
{
Employees = new List<Employee>();
for (int i = 0; i < 100; ++i)
{
Employees.Add(new Employee() { Name = "Gema Arterton" });
}
}
}
public class Employee
{
public string Name { get; set; }
}
(sorry for my bad english)
It depends too on what control is the listbox inside. If your listbox is inside a Grid, it should take all the space of that grid - if it is inside a stackpanel it will use the least amount os space possible so you will have to use fixed height/width. Try this:
<UserControl...>
<Grid>
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</ListBox>
</Grid>
</UserControl/>
The listbox should use all screen size and show vertical scrollbars if there is enough items.
You can set the height in scrollviewer
<ScrollViewer VerticalScrollBarVisibility="Visible" Height="480">
<Grid Margin="0,0,0,50">
</Grid>
</ScrollViewer>

Items in ItemControls size automatically

I have the next problem. I have a ItemsControl defined with xaml.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Button Grid.Row="0" ></Button>
<ItemsControl Grid.Row="1" x:Name="ItemsControl"></ItemsControl>
</Grid>
In the codebehind I have this ItemsControl be filled with buttons.
public Window1()
{
InitializeComponent();
var list = new List<Button>();
list.Add(new Button() { Name = "btn1", Visibility = Visibility.Visible});
list.Add(new Button() { Name = "btn2", Visibility = Visibility.Collapsed});
list.Add(new Button() { Name = "btn3", Visibility = Visibility.Collapsed});
ItemsControl.ItemsSource = list;
}
Now when the buttons are rendered the height is very small. I want the buttons to be rendered at the max height of the ItemsControl. Is there a way?
try this answer out, I had a go and it worked nicely :D
Stretching controls to fill ItemsControl
Don't forget <RowDefinition Height="*" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" ></Button>
<ItemsControl Grid.Row="1" x:Name="ItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>

Resources