I am trying to dynamically add checkboxes to an uniformgrid in wpf.
But it looks like the grid doesn't allocate them enough space and so they all kinda lay over each other.
This is how I add them in code behind:
foreach (string folder in subfolders)
{
PathCheckBox chk = new PathCheckBox();
chk.Content = new DirectoryInfo(folder).Name;
chk.FullPath = folder;
chk.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
chk.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
unfiformGridSubfolders.Children.Add(chk);
}
This is how my XAML looks (I placed the uniformgrid in a scrollviewer)
<ScrollViewer Grid.Column="1" Grid.RowSpan="3">
<UniformGrid x:Name="unfiformGridSubfolders" Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</ScrollViewer>
And this is how it looks in the program:
I just want that every checkBox has enough space, so that the content can be fully read.
do you have to add UI elements dynamically? can't you just predefine your CheckBox template and add CheckBox.Content instead? If it's possible then define an ObservableCollection that contains your CheckBox.Contentlike this:
public ObservableCollection<string> SubfolderNames { get; set; }
then define an ItemsControl and bind it's ItemsSource to your collection:
<ItemsControl Grid.Row="0" x:Name="gridSubfolders" ItemsSource="{Binding SubfolderNames}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="50" />
</Grid.ColumnDefinitions>
<Border Margin="5" BorderThickness="1" BorderBrush="Black">
<CheckBox Content="{Binding}"/>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This way, All Items have the same width as they share a size group, moreover because they are sized Auto, they will also size to the largest CheckBox.Content.
I would suggest using something like WrapPanel
Then how can I do it, that each cell has the size of the checkBox with the biggest content?
Using a UniformGrid You would have to manually go through each checkbox, checking its size, and modifying the Uniform Grid.Columns to something like Math.Floor(Grid.CurrentWidth / CheckBoxMaxWidth)
When the Rows and the Columns property of a UniformGrid are both set to zero the UniformGrid tries to layout the elements in a square without regarding the size of the elements. I'd write a panel that layouts your elements as you want it like the following one. Just use MyPanel instead of UniformGrid in your XAML.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace MyNamespace
{
class MyPanel : Panel
{
int columns;
int rows;
protected override Size MeasureOverride (Size constraint)
{
Size childConstraint = constraint;
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
if (InternalChildren.Count > 0)
{
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
UIElement child = InternalChildren[i];
// Measure the child.
child.Measure (childConstraint);
Size childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
{
maxChildDesiredWidth = childDesiredSize.Width;
}
if (maxChildDesiredHeight < childDesiredSize.Height)
{
maxChildDesiredHeight = childDesiredSize.Height;
}
}
columns = (int)(constraint.Width / maxChildDesiredWidth);
rows = (int)(InternalChildren.Count / columns + 0.5);
}
else
{
columns = 0;
rows = 0;
}
return new Size ((maxChildDesiredWidth * columns), (maxChildDesiredHeight * rows));
}
protected override Size ArrangeOverride (Size arrangeSize)
{
Rect childBounds = new Rect (0, 0, arrangeSize.Width / columns, arrangeSize.Height / rows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
childBounds.X += 0;
foreach (UIElement child in InternalChildren)
{
child.Arrange (childBounds);
if (child.Visibility != Visibility.Collapsed)
{
childBounds.X += xStep;
if (childBounds.X >= xBound)
{
childBounds.Y += childBounds.Height;
childBounds.X = 0;
}
}
}
return arrangeSize;
}
}
}
Related
I was intending to overlay multiple Canvas, between 4 and 6 layers, on top of a large Image, in order that I can set all objects in a given Canvas as visible or invisable with the simplicity of a show or Hide routine in a layer class. UpdateLayers simply has a set of calls to each layer.Update(). In the case of the settlementNames layer, it would seem that the Update code is not doing its job. It is supposed work like this;
private void ShowCities_Click(object sender, RoutedEventArgs e)
{
UpdateLayers();
settlements.Show(Settlements);
settlementNames.Show(SettlementNames);
}
public void Show(Canvas canvas)
{
canvas.Visibility = Visibility.Visible;
}
This worked perfectly with the first canvas containing icon sized BitmapImages at ZIndex 1 (the large Image is essentially the background with ZIndex 0). When I tried to add a further canvas at ZIndex 2, the code steps through as expected but does not show the contents. This time the contents is a set of TextBlocks.
The AssociatedCanvas property in the code, has been checked and is the correct Canvas instance, which was laid down in the XAML main window.
public void Update(string layerSelectSqlQuery, LayerType layerType)
{
DataTable layerDataTable = null;
int x = -1;
int y = -1;
string label;
using (MySqlClientWrapper db = new MySqlClientWrapper("Server = localhost; Database = tribes;Uid = root;Pwd = xxxxxxxxx;"))
{
// TODO add population column - and filter to those settlements considered cities.
layerDataTable = db.GetDataTable(layerSelectSqlQuery);
}
AssociatedCanvas.Children.Clear();
foreach (DataRow dataRow in layerDataTable.Rows)
{
x = (int)dataRow["MapX"];
y = (int)dataRow["MapY"];
label = dataRow["Name"].ToString();
if (x != -1 && y != -1)
{
switch (layerType)
{
case LayerType.Settlements:
DrawBitmapImage(x, y);
break;
case LayerType.SettlementNames:
WriteLabel(x, y, label, Color.FromRgb(0, 0, 0));
break;
case LayerType.Units:
break;
case LayerType.UnitNames:
break;
default:
break;
}
}
}
}
Public void WriteLabel(int x, int y, string text, Color color)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = text;
textBlock.Foreground = new SolidColorBrush(color);
Canvas.SetLeft(textBlock, x);
Canvas.SetTop(textBlock, y);
AssociatedCanvas.Children.Add(textBlock);
}
The XAML looks like this in part:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--<Slider Grid.Column="0" Orientation="Vertical" HorizontalAlignment="Left" Minimum="1" x:Name="slider" />-->
<ScrollViewer Name="mapScroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid Name="grid" RenderTransformOrigin="0.5,0.5">
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleTransform" />
</TransformGroup>
</Grid.LayoutTransform>
<Viewbox Grid.Column="0" Grid.Row="0" >
<Image x:Name="MainMap" UseLayoutRounding="True" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center"
MouseLeftButtonUp="MainMap_MouseLeftButtonUp" Source="{Binding MainTerrainMap}"></Image>
</Viewbox>
<Canvas x:Name="Settlements" Panel.ZIndex="1" />
<Canvas x:Name="SettlementNames" Panel.ZIndex="2" >
</Canvas>
</Grid>
</ScrollViewer>
</Grid>
I want to be able to dynamically set a window's size in WPF, and then create as many columns and rows in a grid as I want.
I've created a method for it but it does not seem to work. I also added a border in XAML to see if there are columns and rows but I only see one square.
Also, it throws no error whatsoever.
This is my method:
public void CreateField(Grid MainGrid, TextBox Columns, TextBox Rows, TextBox WHeight, TextBox WWidth, MainWindow MainWindow)
{
int ColumnCount = Int32.Parse(Columns.Text);
int RowCount = Int32.Parse(Rows.Text);
int WindowHeight = Int32.Parse(WHeight.Text);
int WindowWidth = Int32.Parse(WWidth.Text);
MainWindow.MainWindow1.Height = WindowHeight;
MainWindow.MainWindow1.Width = WindowWidth;
for(int a = 0; a <= ColumnCount; a++){
ColumnDefinition c = new ColumnDefinition();
c.Width = new GridLength(WindowWidth / ColumnCount, GridUnitType.Pixel);
MainGrid.ColumnDefinitions.Add(c);
}
for (int a = 0; a <= RowCount; a++)
{
RowDefinition r = new RowDefinition();
r.Height = new GridLength(WindowHeight / RowCount, GridUnitType.Pixel);
MainGrid.RowDefinitions.Add(r);
}
}
In XAML I have this good with only 1 column and 1 row and a dockpanel for the textboxes and buttons.
<Border BorderBrush="Black" BorderThickness="2">
<Grid Name="MainWindowGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Name="DockPanel"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Background="LightSalmon" Grid.Row="0" Grid.Column="0" Grid.RowSpan="8">
<StackPanel>
<TextBox Name="txtColums" Text="16"/>
<TextBox Name="txtRows" Text="8"/>
<TextBox Name="txtWindowHeight" Text="800"/>
<TextBox Name="txtWindowWidth" Text="1600"/>
<Button x:Name="ButtonCreate" Content="Create" Click="ButtonCreate_Click"/>
</StackPanel>
</DockPanel>
<ContentControl Content="{Binding}"/>
</Grid>
</Border>
And according to the parameters written here the method should be executed on button click. But instead of a grid with 16 columns and 8 rows, I only get 1 column 1 row. (you can see the border at the edge)
So what am I doing wrong here? I have no real experience with grids whatsoever, and I am pretty clueless. Hope someone can help me out.
EDIT:
The suggestion of to activate MainGrid.ShowGridLines as said by ASh worked. Did not know about this functionality. As it turns out i do have succesfully created the columns and rows. I thought it not to work because i tried to paint a field in the grid with a color which did not work. Now i wonder, why this does not work as i thought this to be correct code.
var converter = new System.Windows.Media.BrushConverter();
var brush1 = (Brush)converter.ConvertFromString("#FFFFFFF0");
DockPanel myDockPanel = new DockPanel();
Grid.SetColumn(myDockPanel, 3);
Grid.SetRow(myDockPanel, 3);
myDockPanel.Background = brush1;
There was a lot going wrong here and its a magical jar of wonderment why you wanted to do this, however this should point you in a better direction
Some Modifications
Dedicated Grid
Get the ActualHeight and ActualWidth to use
Set GridLines true, so you can see whats happening
Set the Grid alignments to Stretch
Don't alter the size of the window
Xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
BorderBrush="Black"
BorderThickness="2">
<DockPanel Grid.RowSpan="8" Background="LightSalmon">
<StackPanel>
<TextBox Name="txtColums" Text="16" />
<TextBox Name="txtRows" Text="8" />
<TextBox Name="txtWindowHeight" Text="800" />
<TextBox Name="txtWindowWidth" Text="1600" />
<Button x:Name="ButtonCreate"
Click="ButtonCreate_OnClick"
Content="Create" />
</StackPanel>
</DockPanel>
</Border>
<Grid Name="MainWindowGrid"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ShowGridLines="True" />
</Grid>
Code behind
public void CreateField(Grid MainGrid, TextBox Columns, TextBox Rows) // TextBox WHeight, TextBox WWidth, MainWindow MainWindow)
{
var ColumnCount = int.Parse(Columns.Text);
var RowCount = int.Parse(Rows.Text);
var width = MainGrid.ActualWidth;
var height = MainGrid.ActualHeight;
for (var a = 0; a <= ColumnCount; a++)
{
var c = new ColumnDefinition();
c.Width = new GridLength(width / ColumnCount, GridUnitType.Pixel);
MainGrid.ColumnDefinitions.Add(c);
}
for (var a = 0; a <= RowCount; a++)
{
var r = new RowDefinition();
r.Height = new GridLength(height / RowCount, GridUnitType.Pixel);
MainGrid.RowDefinitions.Add(r);
}
}
private void ButtonCreate_OnClick(object sender, RoutedEventArgs e)
{
CreateField(MainWindowGrid, txtColums, txtRows);
}
grid layout is correct. set MainGrid.ShowGridLines=true; to see it
to see dynamically created control you should add it to grid:
MainGrid.Children.Add(myDockPanel);
since grid rows have equal height and columns have equal width, the following lines can be safely removed:
r.Height = new GridLength(WindowHeight / RowCount, GridUnitType.Pixel);
c.Width = new GridLength(WindowWidth / ColumnCount, GridUnitType.Pixel);
if Width/Height is not set, it is defaulted to * which means Grid will size them equally.
as an alternative UniformGrid can be used:
AnotherMainGrid = new UnifromGrid {Rows = RowCount, Columns = ColumnCount };
but in this case child elements must be added consequtively
I'm having a problem writing sensible logic when displaying a dynamic number of controls, which could range from any number from 1 to 9. So, if user input is 1, the control should attempt to fill the screen, if the user input is 2 the two controls should split the screen evenly, if the number is 3 one control would display on the top 50% of the screen while two controls should split the bottom 50% of the screen etc.
The solution I've come up with till now involves making a ton of grid rows and columns in the code behind that, depending on user input, assigns the controls to the right rows and columns. However, this solution feels like a hack and results in lots of unnecessary code. It's also not at all flexible if I want to expand the number of controls later.
I have a feeling there has to be an easier way to approach this problem, any suggestions?
You can extend Grid or a similar control and override it's layout behavior for your custom logic, while not having to re-invent the wheel.
For example, you can create a dynamic-grid control in following manner (it works with any number of children and automatically adjusts the number of rows and columns):
public class DynamicGrid : Grid
{
public static readonly DependencyProperty AdjustColumnWidthProperty =
DependencyProperty.RegisterAttached("AdjustColumnWidth",
typeof(double),
typeof(DynamicGrid),
new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsArrange));
public static double GetAdjustColumnWidth(DependencyObject d)
{
return (double)d.GetValue(AdjustColumnWidthProperty);
}
public static void SetAdjustColumnWidth(DependencyObject d, double value)
{
d.SetValue(AdjustColumnWidthProperty, value);
}
private int getSquareLength(int items)
{
double result = Math.Sqrt(items);
return (int)Math.Ceiling(result);
}
private int getColumns(int length)
{
return length;
}
private int getRows(int length)
{
var count = _currentChildrenCount;
//assume we can have empty row
var rows = length - 1;
//if fits the bill - great!
if (rows * length >= count)
return rows;
else
return rows + 1;
}
private int _currentChildrenCount;
private void OnNumberOfItemsChangedImpl()
{
var numOfChildren = _currentChildrenCount;
using (var d = Dispatcher.DisableProcessing())
{
RowDefinitions.Clear();
ColumnDefinitions.Clear();
if (numOfChildren > 0)
{
var squareLength = getSquareLength(numOfChildren);
var numOfCols = getColumns(squareLength);
var numOfRows = getRows(squareLength);
for (var i = 0; i < numOfRows; i++)
RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
for (var i = 0; i < numOfCols; i++)
ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
var adjustWidthFactor = 1.0;
var adjustWidthOnLastRow = numOfChildren < (numOfCols * numOfRows);
if (adjustWidthOnLastRow)
{
var notEmptySlots = (numOfChildren % numOfCols);
adjustWidthFactor = ((double)numOfCols / (double)notEmptySlots);
}
int row = 0, col = 0;
foreach (var view in Children)
{
var cell = (FrameworkElement)view;
SetRow(cell, row);
SetColumn(cell, col);
if (adjustWidthOnLastRow && row == (numOfRows - 1))
SetAdjustColumnWidth(cell, adjustWidthFactor);
else
SetAdjustColumnWidth(cell, 1.0);
if (++col >= numOfCols)
{
col = 0;
row++;
}
}
}
}
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var toReturn = base.ArrangeOverride(arrangeSize);
foreach (var view in Children)
{
var cell = (FrameworkElement)view;
var adjustWidthFactor = GetAdjustColumnWidth(cell);
var bounds = LayoutInformation.GetLayoutSlot(cell);
var newBounds = new Rect(
x: bounds.Width * adjustWidthFactor * GetColumn(cell),
y: bounds.Top,
width: bounds.Width * adjustWidthFactor,
height: bounds.Height
);
cell.Arrange(newBounds);
}
return toReturn;
}
public DynamicGrid()
{
_currentChildrenCount = 0;
LayoutUpdated += (s, e) => {
if (Children?.Count != _currentChildrenCount)
{
_currentChildrenCount = (Children != null) ? Children.Count : 0;
OnNumberOfItemsChangedImpl();
}
};
}
}
Sample usage 1: - Static collection
<local:DynamicGrid Margin="20">
<Button>one</Button>
<Button>two</Button>
<Button>three</Button>
<Button>four</Button>
<Button>five</Button>
<Button>six</Button>
<Button>seven</Button>
<Button>eight</Button>
</local:DynamicGrid>
Sample usage 2: - With ItemsControl
<ItemsControl Margin="20">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:DynamicGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Gray" Margin="5">
<TextBlock Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsSource>
<col:ArrayList>
<sys:String>one</sys:String>
<sys:String>two</sys:String>
<sys:String>three</sys:String>
<sys:String>four</sys:String>
<sys:String>five</sys:String>
</col:ArrayList>
</ItemsControl.ItemsSource>
</ItemsControl>
Sample usage 3: - Dynamic collection
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl>
<ItemsControl.ItemsSource>
<Binding Path="Value" ElementName="slider">
<Binding.Converter>
<local:CountToCollectionConverter />
</Binding.Converter>
</Binding>
</ItemsControl.ItemsSource>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Gray" Margin="5">
<TextBlock Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:DynamicGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Slider x:Name="slider"
Grid.Row="1"
Minimum="1"
Maximum="12"
TickFrequency="1"
IsSnapToTickEnabled="True"
VerticalAlignment="Center" />
</Grid>
How it works
Whenever the children collection on the Grid is updated, it tries to find the nearest perfect square to the children-count. Once found, it calculates the number of columns and rows based on computed square-length; and defines the RowDefinitions, and ColumnDefinitions accordingly. If there is space left in last row, it adjusts the width of controls to fill the space.
Please note: As no specific rules have been specified in the question, I have customized this grid to just adjust the items in last row
You could use a StackPanel for each row and one for your "grid" like this
<StackPanel>
<StackPanel/>
<StackPanel/>
</StackPanel>
You could then add and fill your rows as required to match the layout for your given configuration - e.g. for 3 controls - 2 in row 1 and 1 in row 2, for 4 controls - 2 in row 1 and 2 in row 2, etc...
You could then bind the widths of the controls in each row to a property that was a function of how many controls there were in that particular row. For example, if there were 3 in a row each control would be 1/3 of the width. As long as you update this width property whenever you modify a row the control widths will be updated to fill your available space.
To add additional layouts you simply need to add new rules to your layout configuration.
I have been looking for a resizeable Universal Windows App (RT, UWP) control for handling different screen sizes and scalable controls. What I am looking for is something like a wrapgrid (What I am using below), except that it changes the column width to fill the space when it is resized, like what occurs with the Tubecast app for windows, when you resize the window the columns will expand, or when shrinking, merge once they hit a minimum value.
Currently I am using a wrapgrid control to fill the TV shows into the library, adding a new frame in code, navigating it to a new instance of the LibraryModel Page, passing a class via the onNavigatedTo() method. This XAML page has a min properties of 135x200, and a max properties of 270x400, using static item height and with of 270x400 and visual state groups to change to 125x200 when the width goes below 720px. I tried using a variablesizedwrapgrid, but it wasn't any more helpful.
Is there a control like this that exists for UWP apps? Or will it need to be created manually using C#, or added to the platform later? This control is likely essential for future Windows 10 App development.
Example Screenshot
I suggest you look at view-boxes, might provide a solution.
I figured out a way to make the controls scale to screen sizes, so that they will take up all available real estate, and works well on all devices.
Others shown at bottom of page..
<Grid Background="#FF1D1D1D" x:Name="maingrid" SizeChanged="maingrid_SizeChanged">
<Grid Grid.ColumnSpan="2" Grid.RowSpan="2">
<ScrollViewer x:Name="LibraryScroll">
<Grid>
<Viewbox x:Name="LibraryItemViewbox" Stretch="Uniform" VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid x:Name="Area" Width="{x:Bind maingrid.Width}" Height="{x:Bind maingrid.Height}">
<ItemsControl x:Name="showsPanel" Loaded="showsPanel_Loaded" ItemsSource="{x:Bind Library.LibraryItems, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid x:Name="shows" Orientation="Horizontal" ItemHeight="400" ItemWidth="270" MaximumRowsOrColumns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="viewmodel:LibraryItemModel">
<Button Padding="0" Foreground="Transparent" BorderThickness="0" Tapped="LibraryItem_Tapped" RightTapped="LibraryItem_RightTapped" Holding="Button_Holding"/>
<Grid x:Name="MainGrid" Background="#00A6A6A6" Width="270" Height="400">
<!-- Content -->
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Viewbox>
</Grid>
</ScrollViewer>
</Grid>
</Grid>
This is the XAML structure required to scale the content.
The Viewbox is wrapped into a Grid, so that Vertical and Horizontal Alignment still works inside the ScrollViewer. The inner Grid "Area" has its Height and Width bound to the base Grid 'maingrid', so it maintains the aspect ratio of the page.
The Itemscontrol is defined as a WrapGrid, meaning that Item Width has to be defined, meaning this won't work variable sized controls inside (Although possible with some modification). The ItemTemplate is then defined as well (Requiring the Base Grid 'MainGrid' to be the same dimensions as the WrapGrid's ItemWidth and ItemHeight).
Events that are required are SizeChanged on the Base Grid and Loaded on the ItemsControl.
In order to scale the elements when the page is loaded, and scale them when the page is resized, the code looks like this:
private void showsPanel_Loaded(object sender, RoutedEventArgs e)
{
Area.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Resize();
fillGaps(showsPanel.ItemsPanelRoot as WrapGrid);
}
private void Resize()
{
var width = this.ActualWidth;
var height = this.ActualHeight;
var grid = (WrapGrid)showsPanel.ItemsPanelRoot;
int numofColsOrig = grid.MaximumRowsOrColumns;
if (width >= 2800) grid.MaximumRowsOrColumns = 8;
if (width < 2800) grid.MaximumRowsOrColumns = 8;
if (width < 2400) grid.MaximumRowsOrColumns = 7;
if (width < 2000) grid.MaximumRowsOrColumns = 6;
if (width < 1600) grid.MaximumRowsOrColumns = 5;
if (width < 1200) grid.MaximumRowsOrColumns = 4;
if (width < 800) grid.MaximumRowsOrColumns = 3;
if (width < 400)
{
grid.MaximumRowsOrColumns = 2;
if (Library.LibraryItems.Count >= 2) Area.Padding = new Thickness(0);
}
if (numofColsOrig != grid.MaximumRowsOrColumns)
{
fillGaps(grid);
}
}
private void fillGaps(WrapGrid grid)
{
var libraryitems = Library.LibraryItems;
if (libraryitems.Count < grid.MaximumRowsOrColumns && libraryitems.Count != 0)
{
int numOfItemsToFill = grid.MaximumRowsOrColumns - libraryitems.Count;
Area.Padding = new Thickness { Right = (grid.ItemWidth * numOfItemsToFill) };
}
}
private void maingrid_SizeChanged(object sender, SizeChangedEventArgs e) { Resize(); }
The values of widths to change the number of rows will need to be manually tweaked in order to look better with different size objects, and when adding or removing from the ItemSource, Resize(); will have to be called to recalculate the dimensions of the elements, for it to look correct.
You will, of course, need to replace libraryitems with you own ObservableCollection, so that it can get the count of how many objects are in your list, or get the count from your WrapGrid's items count.
This question already has an answer here:
Reusing Text views and Grid
(1 answer)
Closed 8 years ago.
I am dynamically creating say 300 views in For loop :
Ex:
for (int j = 0; j <= 300; j++)
{
Image image = new Image();
image.Source = new BitmapImage(new Uri("/Images/sample256.png", UriKind.RelativeOrAbsolute));
Grid titleGrid = new Grid();
titleGrid.HorizontalAlignment = HorizontalAlignment.Center;
titleGrid.VerticalAlignment = VerticalAlignment.Center;
TextBlock titleText = new TextBlock();
titleText.TextWrapping = TextWrapping.Wrap;
titleGrid.Children.Add(titleText);
Grid subtitleGrid = new Grid();
subtitleGrid.HorizontalAlignment = HorizontalAlignment.Center;
subtitleGrid.VerticalAlignment = VerticalAlignment.Center;
TextBlock subtitleText = new TextBlock();
subtitleText.TextWrapping = TextWrapping.Wrap;
subtitleGrid.Children.Add(subtitleText);
//add all views to root layout
LayoutRoot.Children.Add(image);
LayoutRoot.Children.Add(titleGrid);
LayoutRoot.Children.Add(subtitleGrid);
}
Now there is a lag in the app as I am adding new view each time, how can I reuse the already created views? I am working on Windows Phone 8 app.
adding 300 items in the layout root will definatly make page load slow. you need to use controls that implement virtulization like listbox. here is how
ListBox XAML in your page.
<ListBox Name="myListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding ImageUrl}">
</Image>
<TextBlock Text="{Binding Question}"></TextBlock>
<TextBlock Text="{Binding Answer}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
your code behind
code to bind data
List<MyData> list = new List<MyData>();
for (int i = 0; i < 300; i++)
{
var data = new MyData();
data.Question = "//yourquestion";
data.Answer = "// your answer";
data.ImageSource = new BitmapImage(new Uri("yourimagepat"));
}
myListBox.ItemsSource = list;
Data Class
public class MyData {
public string Question { get; set; }
public string Answer { get; set; }
public BitmapImage ImageSource { get; set; }
}
to take advantage of the virtulization please add your listbox in Grid control. otherwise it can throw out of memory exception and also will be slow
You could try using DataBinding feature instead of creating this .
// You can bound items from your Class here
<ListBox x:Name="ListBox1" Margin="5"
Width="450" Height="200" HorizontalAlignment="Left"
ItemsSource="{Binding SongsList}">
ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel CleanUpVirtualizedItemEvent="VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1">
</VirtualizingStackPanel>
</ItemsPanelTemplate>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="Artist:" Margin="2" />
<TextBlock Text="{Binding Artist}" Margin="2" />
<TextBlock Text="CD:" Margin="10,2,0,2" />
<TextBlock Text="{Binding Name}" Margin="2" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
// your class should be like this
public Songs
{
public string Artist{get;set;}
public string Name {get;set;}
}
// CodeBehind Just Add Data
ObservableCollection<Songs> SongsList=new ObservableCollection<SongsL();
for (int j = 0; j <= 300; j++)
{
SongsList.Add(new Songs{Artist="Aritst Name",Name="Song Name"});
}
// Set this Collection from the codebehind or xaml .
ListBox1.ItemSource=SongsList; // it will the bind the public properties in this Songs.
DataBinding Controls on Windows phone
Msdn Databindin greference
Binding data Grid
Databinding from Jesse Liberty