Coverflow with Out of Memory - c#

i am working on Windows 8 Phone Application.
I have issue where i am loading image with text on top of the images one by one.Its called coverflow feature.
I am getting Out of memory exception
for (int j = 0; j < items.Count; j++)
{
for (int i = 0; i < items.Collection.Count; i++)
{
Myobj obj = items[j].Collection[i];
if (obj.correct == 1)
{
coverflow.Add(new CoverFlow(items[j].Text, answer.TextTwo));
}
}
}
CarouselList.ItemsSource = coverflow;
DataTemplate :
<DataTemplate x:Key="DataTemplate1">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Height="400" Width="400" CornerRadius="30,30,30,30">
<Border.Background>
<ImageBrush ImageSource="Images/sample.png" />
</Border.Background>
</Border>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="5,20,5,5"
Foreground="#000000"
Text="{Binding Title}"/>
</Grid>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Bottom">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="5,5,5,20"
Foreground="#000000"
Text="{Binding SubTitle}"/>
</Grid>
</Grid>
</DataTemplate>
Here the there are around 300+ items that displays one after the other:
Like this :
Its not working at all i tried to reduce the widht and height from 400 to 200 it works but i want the image size to 400 so that it looks good.
how can i avoid this Out Of Memory even if my images are 400*400

This is going to be really from the top of my head. Haven't dealt with this in a while.
1.Write yourself a function which will return you a bunch of items
public List<Item> GetFirstItems()
{
return items.Collection.Take(50);
}
public Item GetOtherItems(int skip)
{
return items.Collection.Skip(skip).Take(25)
}
2.Hook up to the SelectionChangedEvent for your control
//keep this somewhere so you know where you are in the list
var currentBatch = 0;
private void SelectionChanged(sender object, ChangedEventArgs e)
{
var position = e.CurrentItemIndex % 25;
if(position > currentBatch)
{
currentBatch = position;
var newItems = GetOtherItems(currentBatch * 25);
//take the global list of items and modify it;
//because we are moving right we only need the last 25 so we
//can skip the first 25
coverflow= coverflow.Skip(25);
//add your new items
coverflow.AddRange(newItems);
CarouselList.ItemsSource = coverflow; // you will have to clear the source first
}
else if(position < currentBatch)
{
currentBatch = position;
var newItems = GetOtherItems(currentBatch * 25);
//take the global list of items and modify it;
//because we are moving left we only need the first 25 so we
//can take the first 25
coverflow= coverflow.Take(25);
//add your new items
newItems.AddRange(coverflow);
coverflow = newItems;
CarouselList.ItemsSource = coverflow; // you will have to clear the source first
}
}
One more thing you will have to take care of is memorizing which was the current item and setting it again to be the current item.
This is all written from the top of my head and I have no idea if it work with your control but I hope it will at least help you a bit :)

Related

Why do the TextBlocks in my Canvas not display?

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>

Set Dockpanel size in grid programmatically

My goal is to programmatically set a DockPanel size.
I want it to span from Grid.Column=1, Grid.Row=1, Grid.RowSpan=5
And I know hot to set it statically in xaml, but not in c#.
Explanation to code: In xaml I made a 1row 1 column grid with some textfields and a button in the DockPanel. In when I press the button it should create a grid with as many column/rows as I wrote in the textfields. Then name each column and each row. And know I want to create a Dockpanel on some of these fields but for that I must define where it starts and how far it spans. This is where the problem is.
here is my xaml code how I made it:
<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>
And my C# code what I have so far:
Methods Methods = new Methods();
Methods.CreateField(MainWindowGrid, txtColums, txtRows, txtWindowHeight, txtWindowWidth, MainWindow1);
int GridColumnCount = MainWindowGrid.ColumnDefinitions.Count;
int GridRowCount = MainWindowGrid.RowDefinitions.Count;
for (int a = 1; a < GridColumnCount; a++)
{
MainWindowGrid.ColumnDefinitions.ElementAt(a).Name = "C" + a;
}
for (int a = 1; a < GridRowCount; a++)
{
MainWindowGrid.RowDefinitions.ElementAt(a).Name = "R" + a;
}
var converter = new System.Windows.Media.BrushConverter();
var brush1 = (Brush)converter.ConvertFromString("#FFFFFFF0");
DockPanel myDockPanel = new DockPanel();
myDockPanel.Background = brush1;
myDockPanel.
At the very end I want to be able to set at which row/column the dockpanel should be and then span it, but I sadly do not know how.
You could use the following methods to set the Grid.Column, Grid.Row and Grid.RowSpan attached properties of myDockPanel:
Grid.SetColumn(myDockPanel, 1); //= <DockPanel ... Grid.Column = "1"
Grid.SetRow(myDockPanel, 1); //= <DockPanel ... Grid.Row = "1"
Grid.SetRowSpan(myDockPanel, 8); //= <DockPanel ... Grid.RowSpan = "8"

Displaying a dynamic number of controls

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.

Accessing XAML elements from C# using string or arrays

I am new to WindowsApp Development and I was trying to access XAML elements through C# code using an array.
For example I have few ellipses in my XAML code-
<Ellipse Fill="#FFF4F4F5" x:Name="E_0" Grid.Row="0" Grid.Column="2" Stroke="Black" RenderTransformOrigin="0.474,5.849" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Ellipse Fill="#FFF4F4F5" x:Name="E_1" Grid.Row="0" Grid.Column="3" Stroke="Black" RenderTransformOrigin="0.474,5.849" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Ellipse Fill="#FFF4F4F5" x:Name="E_2" Grid.Row="0" Grid.Column="4" Stroke="Black" RenderTransformOrigin="0.474,5.849" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center"/>
Now I want to work with them in C# in a loop so I am doing something similar to the following-
string s1="E_";
double width=500;
for (int i = 0; i < 2;i++ )
{
string name_i = s1 + i.ToString();
name_i.Width = width / 2;
}
But name_i.width gives me an error. So, do I have to use the actual names, is there no way I can use an array or string? Using the actual names will defeat the purpose as I have about 50 such elements which I need to work upon.
As Maximillian proposed, you can use the parent container and iterate over its children:
Xaml:
<StackPanel Name="StackPanelContainer">
<Grid></Grid>
<Ellipse Name="E_0"></Ellipse>
<Ellipse Name="E_1"></Ellipse>
<Ellipse Name="E_2"></Ellipse>
</StackPanel>
Codebehind:
//if you are sure every child element is a ellipse, you can use:
foreach (Ellipse child in StackPanelContainer.Children)
{
child.Width = 100;
}
//if there are also other elements, and also check if name starts with "E_"
foreach (object child in StackPanelContainer.Children)
{
var ellipse = child as Ellipse;
if (ellipse != null && ellipse.Name.StartsWith("E_"))
{
ellipse.Width = 100;
}
}
Use FindName() method.
string s1 = "E_";
double width = 500;
for (int i = 0; i < 2; i++)
{
string name_i = s1 + i.ToString();
var ellipse = FindName(name_i) as Ellipse;
if (ellipse != null)
{
ellipse.Width = width / 2;
}
}

WPF Charts - Rotate XAXIS labels

I am hoping someone can help me. I am an amateur developer (at best) who mainly writes code to help parse log files and I am struggling with charting.
In the code below, I am simply taking in any DataTable (with properly named columns and datatypes) and creating a stacked chart using the WPF toolkit. I am having a very hard time rotating the XAXIS labels.
I have looked at all of these links and tried to integrate them into what I have already done, but nothing is working or I am not exactly sure how to interpret these links. Can anyone guide me through how to rotate my XAXIS after viewing how my chart gets created?
Silverlight: How to change AxisLabelStyle in code behind?
http://blogs.msdn.com/b/delay/archive/2010/03/06/turn-your-head-and-check-out-this-post-how-to-easily-rotate-the-axis-labels-of-a-silverlight-wpf-toolkit-chart.aspx
Create style to rotate axis label in code behind
Here is my code:
XAML:
<Window x:Class="gtseq_stats.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main" Height="451" Width="685" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" RenderTransform="{Binding StringFormat=\{0:g\}}">
<Grid ForceCursor="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="552*" />
<ColumnDefinition Width="142*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="2,2,0,0" Name="cbx_report_name" VerticalAlignment="Top" Width="320" DisplayMemberPath="report_name_alias" SelectedValuePath="report_data_id" SelectionChanged="cbx_report_name_SelectionChanged" />
<toolkit:Chart Margin="10,31,12,12" Name="OutputChart" Grid.ColumnSpan="2" />
<Button Content="Button" Height="26" HorizontalAlignment="Left" Margin="350,2,0,0" Name="button1" VerticalAlignment="Top" Width="53" Click="button1_Click" FlowDirection="RightToLeft" />
</Grid>
</Window>
And then my C#:
public void BuildStackedChart(DataTable dtResults)
{
OutputChart.Series.Clear();
var palette = OutputChart.Palette;
OutputChart.Palette = null;
OutputChart.Palette = palette;
Dictionary<string, int> dictYAxis = new Dictionary<string,int>();
try
{
//Get a list of distinct Y axis and add them to a dictionary. This will be used later so we can assigne values to the proper dataValues.
var distinctYAxis = (from row in dtResults.AsEnumerable()
//orderby row.Field<string>("fix_line") ascending
select row.Field<string>("YAXIS")).Distinct();
int i = 0;
foreach (var name in distinctYAxis)
{
dictYAxis.Add(name, i);
i++;
}
}
catch
{
MessageBox.Show("Couldn't Properly Load Data. The data must have only 3 columns: XAXIS-DateTime Data Type" + Environment.NewLine + "YAXIS-String Value" + Environment.NewLine + "PLOTVALUES-Number");
return;
}
var dataValues = new List<List<SimpleDataValue>>();
try
{
//Add a new entry for however many YAxis we have (No Data is used at this point are we aren't linking the #'s in the dictionary to the data elements here.
//However, we will use the dictorary to add the enteries and use the numbers to respresent the rows.
foreach(KeyValuePair<string, int> pair in dictYAxis)
{
dataValues.Add(new List<SimpleDataValue>());
}
foreach (DataRow row in dtResults.Rows) // Loop over the rows.
{
dataValues[dictYAxis[row["YAXIS"].ToString()]].Add(new SimpleDataValue { DependentValue = Convert.ToDouble(row["PLOTVALUES"]), IndependentValue = Convert.ToDateTime(row["XAXIS"])});
}
}
catch
{
dataValues.Clear();
}
int i2 = 0;
var stackedSeries = Activator.CreateInstance(typeof(StackedColumnSeries)) as DefinitionSeries;
foreach (var values in dataValues)
{
var definition = new SeriesDefinition();
definition.DependentValuePath = "DependentValue";
definition.IndependentValuePath = "IndependentValue";
definition.Title = dictYAxis.FirstOrDefault(x => x.Value == i2).Key; ;
definition.ItemsSource = values;
stackedSeries.SeriesDefinitions.Add(definition);
i2++;
}
OutputChart.Series.Add(stackedSeries);
}
Check this post [How to: Easily rotate the axis labels of a Silverlight/WPF Toolkit chart]
<charting:ColumnSeries.IndependentAxis>
<charting:CategoryAxis
Orientation="X">
<charting:CategoryAxis.AxisLabelStyle>
<Style TargetType="charting:AxisLabel">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="charting:AxisLabel">
<layout:LayoutTransformer>
<layout:LayoutTransformer.LayoutTransform>
<RotateTransform Angle="-60"/>
</layout:LayoutTransformer.LayoutTransform>
<TextBlock Text="{TemplateBinding FormattedContent}"/>
</layout:LayoutTransformer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</charting:CategoryAxis.AxisLabelStyle>
</charting:CategoryAxis>
</charting:ColumnSeries.IndependentAxis>

Categories