WPF color matrix - c#

I have created a Class called matrix (Matrix [,] matrix;) and there I have the i and j attributes, which refer to the corresponding row and column.
Let's say, for example, that the [1,6] cell contains the number 6 (the content does not really matter as it is an example), or I want to represent it with a red color since 6 is greater than 5.
What I was wondering is what is the best and easiest way to represent this matrix in a Grid (Grid, Datagrid, ...) and for example change the [1,6] element of this grid to the red color, displaying it as the original matrix. This is an example I had but it dows not work, in fact it does not even create a Grid:
System.Data.DataTable dt = new System.Data.DataTable();
...loop for dt...
dt.Columns.Add();
dt.Rows.Add();
datagrid1.DataSource = dt;
Thanks in advance!

This is just one of many possible solutions:
<ItemsControl ItemsSource="{Binding SomeMatrix}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" Columns="{Binding SomeMatrix.Columns}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding}" HorizontalContentAlignment="Center" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
U can then change the appearance of individual cells by modyfying the item template. The only prerequisite is that your Matrix class should implement IEnumerable interface and should yield items in a column-by-column, row-by-row order.

Related

WPF: How to dynamically create a grid with x rows and y columns with consecutive numbers

I am completely new to wpf and c#, so excuse if this is super trivial question. I am trying to create a fairly simple control.
This grid will always have consecutive numbers, with a color rectangle in front of it. Clicking on the gray rectangle will change its color, and set the text to bold (I will deal with these triggers later).
For now, I just need to figure out how to create this control dynamically. When the program starts, it needs to one time create this control, and then the size won't change. I need to tell it the number of columns and rows (each column will probably always have 8 elements), and have it populate with consecutive numbers with specific font style/rectangle color.
I was experimented with creating a stackpanel UserControl for rectangle/label combo, passing the style to it, and then adding 32 of these UserControls in specific row/column in a grid. But I would need the size of that grid to be dynamic, so I need some for loop in the code I think.
Thanks!
I would start with an ItemsControl
You can give it a collection of items, and it will render each item however you want, displayed in any panel you want.
For example, you might have something like this
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- This panel will be used to hold the items -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="8" Columns="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Each item will be drawn using this template -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Text="{Binding }" Style="{StaticResource MyButtonStyle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Rows and Columns property of the UniformGrid are DependencyProperties, so you could bind them to properties on the DataContext to make them dynamic.
The only problem with a UniformGrid is it only arranges items Horizontally. If you want to display them Vertically, you can either create a custom UniformGrid, or switch to a different panel such as a WrapPanel. If you are new to WPF Panels, I would recommend reading through WPF Layouts - A Quick Visual Start.
The ItemTemplate can be anything. Personally I would use a Button so you have the Click or Command behavior to handle that event, and just overwrite the Button's Template to look however you want. It is an easy task to include your Triggers in there too.
And if you wanted selection behavior, I would recommend switching from an ItemsControl to a ListBox, and overwriting that Template the same way, however it doesn't sound like you need it though, so I think an ItemsControl is better :)
I would try using a listview and change the template to the style you want to use for your elements.
To limit the number of items in a row you can use
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
That way you would always get 3 elements in a row, like
123
456
To make the 3 dynamic you can databind it to some value in your codebehind / viewmodel
to dynamically create the elements within the listview you can add objects to a list/observable collection and then add those to the listview via
listviewname.ItemSource=ListName;
Or however you like. They will get arranged according to how many columns you tell the grid to have. Adding 32 items (with uniform grid of 4) leads to
1 2 3 4
5 6 7 8
9 10 11 12
...
On your page you must create a "main" element, for example a Grid.
Give it a name, so that we can access it by code. Here I gave it the name of root
So you will have something like
<Page
... >
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
x:Name="root">
</Grid>
</Page>
Then, on the .cs file of this page you must create a function with the code below. You can call this function on the MainPage() function.
This loop will create one Grid column with dynamic Grid rows
// Create a Grid and add it to a component of your page
Grid mainGrid = new Grid();
root.Children.Add(mainGrid);
for (int i = 0; i < 8; i++)
{
// I am creating a Grid with a TextBlock inside,
// it will have the same apperance as a Rectangle,
// but this way you can have a Text inside
Grid g = new Grid();
TextBlock tb = new TextBlock();
tb.Text = i.ToString();
g.Children.Add(tb);
// Here you set the Grid properties, such as border and alignment
// You can add other properties and events you need
g.BorderThickness = new Thickness(1);
g.BorderBrush = new SolidColorBrush(Colors.Black);
g.HorizontalAlignment = HorizontalAlignment.Stretch;
g.VerticalAlignment = VerticalAlignment.Stretch;
// Add the newly created Grid to the outer Grid
mainGrid.RowDefinitions.Add(new RowDefinition());
mainGrid.Children.Add(g);
// Set the row of the Grid.
Grid.SetRow(g, i);
}
I used a Grid instead of a Rectangle since Rectangles can't have Children.
It must be easy to create the other columns as well, using the same logic that I used to create the Rows.
The solution to me was pretty like the ones above, but I had to bind the ItemsSource in the back code.
<!-- This panel will be used to hold the items -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Each item will be drawn using this template -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Here is the big deal I think. Desing your template according to what you need. In this case, a grid with 1 default row and column will work-->
<Grid>
<!--Reading object property called "Exchange" and setting its value into a text block-->
<TextBlock
Margin="10, 10, 10, 40" FontSize="16"
HorizontalAlignment="Center"
VerticalAlignment="Center" Foreground="GreenYellow" Text="{Binding Exchange}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Then in the page.cs I did the binding right in the constructor. But you can bind anytime suits you
List<Bot> Bots = new()
{
new Bot
{
Exchange = "Binance"
},
new Bot
{
Exchange = "Kukoin"
}
};
ItemsControlName.ItemsSource = Bots; // I HAD to bind here. Could not make it work another way.
In my example, I have used a list of a simple class called Bot, with one single property called Exchange. Like this
public class Bot
{
public string Exchange { get; set; }
}
Then I got it:

How can I display a dynamic number of objects in a nice tile format in WPF?

I would like to make application that can display 1 or 2 videos.
On the left part of the window there will be 2 buttons labeled "1" or "2" as number of tiles I want to display on the right side of the application.
By clicking "1" a video will be played on the entire right side.
By click "2" there will be displayed 2 videos on the right side, in 2 tiles.
For now its only full window tile that display 1 video, and another tile that split the full window to 2 and display 2 videos, but if I want 4 videos I would like to split the main window to 4 and display 4 different videos.
What is the best way to implement this?
Thanks!
Based on what you're saying in comments, it sounds like you want buttons to create a dynamic number of videos and have them displayed nicely in a Grid
I would start by creating an ObservableCollection<VideoPlayer> in your DataContext that holds the number of videos you want, and a second property containing the Square Root of VideoPlayerCollection.Count, rounded up, for determining Grid size.
Then I'd display the VideoPlayerCollection using an ItemsControl that has it's ItemsPanelTemplate set to a UniformGrid or Grid, which binds the row count and column count to your GridSize property.
(You may need to build some AttachedProperties for binding these properties, as Grid's do not have a Row/Column count property, and I can't remember if the UniformGrid's Rows and Columns properties are DependencyProperties or not that you can bind to. I have an example of some AttachedProperties for binding a Grid's RowCount and ColumnCount here if you're interested in an example)
And finally, your Buttons would modify your VideoPlayerCollection to add or remove as many items as you want displayed.
So your final XAML might look something like this:
<DockPanel>
<StackPanel DockPanel.Dock="Left">
<Button Content="One Window"
Command="{Binding ModifyVideoCountCommand}"
CommandParameter="1" />
<Button Content="Two Windows"
Command="{Binding ModifyVideoCountCommand}"
CommandParameter="2" />
<Button Content="Four Windows"
Command="{Binding ModifyVideoCountCommand}"
CommandParameter="4" />
</StackPanel>
<ItemsControl ItemsSource="{Binding VideoPlayerCollection}"
ItemTemplate="{StaticResource VideoPlayerTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="{Binding GridSize}" Columns="{Binding GridSize}" />
</ItemsPanelTemplate>
</ItemsPanelTemplate>
</ItemsControl>
</DockPanel>
While the DataContext behind your form would contain these properties:
ICommand ModifyVideoCountCommand { get; set; }
ObservableCollection<VideoPlayer> VideoPlayerCollection { get; set; }
int GridSize
{
get
{
return Math.Ceiling(Math.Sqrt(VideoPlayerCollection.Count));
}
}
Depending on if you use a Grid or not, you may also need to add RowIndex and ColumnIndex properties to your VideoPlayer class to specify which Grid.Row and Grid.Column each VideoPlayer should be placed in.
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column"
Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row"
Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>

Sorting a listbox containing grid controls in C#

I am writing an app that will display several thumbnails of jpegs with the filename underneath them. I would like to sort these by filename. These jpegs are coming out of a zip file and I cannot receive them in sorted order. I'm using a listbox defined like this:
<ListBox Name="listPanel1" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionMode="Multiple" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Name="wrapPanel1" IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<TextBox Height="152" Name="tb_Messages" Width="244" />
</ListBox>
Then in the code I add an individual grid control for each thumbnail to the listPanel. The first row of the grid is the image, the second is the filename.
Grid grid = new Grid();
ColumnDefinition col0 = new ColumnDefinition();
RowDefinition row0 = new RowDefinition();
RowDefinition row1 = new RowDefinition();
col0.Width = new GridLength(140);
row0.Height = new GridLength(140);
grid.ColumnDefinitions.Add(col0);
grid.RowDefinitions.Add(row0);
grid.RowDefinitions.Add(row1);
grid.Children.Add(thumbnailImage);
grid.Children.Add(lb);
Grid.SetRow(thumbnailImage, 0);
Grid.SetColumn(thumbnailImage, 0);
Grid.SetRow(fileName, 1);
Grid.SetColumn(fileName, 0);
listPanel1.Items.Add(grid);
One of the nice things about this method is that when I select an image both the image and the filename are highlighted.
How can I sort the listbox based on the filename?
This is my first WPF app, so it's entirely possibly that I'm approaching this in the completely wrong way.
Don't create the UI in code! Unless you are creating an user control.
Use a ListBox and bind it datasource to collection of the objects representing the picture
<ListBox ItemSource="{Binding Pictures}"/>
In your view model you will get the names and other properties and Pictures property will return sorted collection (or filtered or whatever)
public IEnumerable<Picture> Pictures
{
get { return _picturesLoadedFromZip.OrderBy(whatever); }
}
To display the thumbnail and file name use template.
<ListBoxItem Background="LightCoral" Foreground="Red"
FontFamily="Verdana" FontSize="12" FontWeight="Bold">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding PathToFile}" Height="30"></Image>
<TextBlock Text="{Binding FileName}"></TextBlock>
</StackPanel>
</ListBoxItem>
More about this you can find here or here.

Polyline Rendering

I have ItemsControl which is bound and creates multiple Polylines, one for each data item.
Currently I get "stack" like display - First Polyline at the top, below is second, below is third, and so on...
How can I make Polylines appear on the top of each other?
You need to change the ItemsPanel of your ItemsControl (which is by default a StackPanel that's why you get the "stack" like display) to another Panel which allows it's Children to be placed on each other like the Canvas or the Grid:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

How do I put multiple HyperlinkButtons on a row in Silverlight?

I have a number of tags (string) in a JSON-formatted stream (resultFromServer) that I put into a list (articleTagList):
if (resultFromServer.tag != null)
{
for (int i = 0; i < resultFromServer.tag.Length; i++)
{
articleTagList.Add(resultFromServer.tag[i]);
}
listboxArticleTags.Items.Clear();
listboxArticleTags.ItemsSource = articleTagList;
}
The listboxArticleTags listbox is using the following data template:
<DataTemplate x:Key="myArticleTagsTemplate">
<HyperlinkButton x:Name="Tag" Content="{Binding Name}"/>
</DataTemplate>
The problem with this is that all tags/HyperlinkButtons end up on one line each:
[Code]
[Example]
[Silverlight]
I want them on a single row:
[Code] [Example] [Silverlight]
This is for a WP7 app which I'm sure limits my options but is this at all possible to do?
Thanks!
This works in regular SL and should also work on the phone. I would also consider switching to ItemsControl instead of ListBox because I don't think that you actually need support for selections in this case.
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Categories