Adding UI Components in C# WPF in Code behind - c#

I'm new to C# and WPF format. In my program, I have an array of strings with text, and I want to create a button in the canvas for each string in the array. I've used flex, and in that i can use the addChild command to put something in something else, but I haven't figured out how to do it in WPF yet. Any help would be appreciated, thanks.

WPF is streams ahead with the ability to use Binding: you can bind an ItemsControl to your array directly, then tell WPF how to display each item with a Template and it will do it.
<!-- ItemsControl is a customisable way of creating a UI element for each item in a
collection. The ItemsSource property here means that the list of items will be selected
from the DataContext of the control: you need to set the DataContext of this control, or
the window it is on, or the UserControl it is in, to your array -->
<ItemsControl ItemsSource="{Binding}">
<!-- The Template property specifies how the whole control's container should look -->
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<ItemsPresenter/>
</ControlTemplate>
</ItemsControl.Template>
<!-- The ItemsPanel tells the ItemsControl what kind of panel to put all the items in; could be a StackPanel, as here; could also be a Canvas, Grid, WrapPanel, ... -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- The ItemTemplate property tells the ItemsControl what to output for each item in the collection. In this case, a Button -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- See here the Button is bound with a default Binding (no path): that means the Content be made equal to the item in the collection - the string - itself -->
<Button Content="{Binding}" Width="200" Height="50"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Hope that helps!
Reference:
http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.aspx

I hope this can get you started (untested!):
foreach ( var s in textArray )
{
Button b = new Button();
//set button width/height/text
...
myCanvas.AddChild( b );
// position button on canvas (set attached properties)
Canvas.SetLeft( b, ... ); // fill in the ellipses
Canvas.SetTop( b, ... );
}
A more advanced technique would let you sync the UI to the contents of your array.
I highly recommend the book "WPF Unleashed" to learn WPF. http://www.adamnathan.net/wpf/

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:

Windows phone proper way to display collection of items

I have XML file with my items and I'm deserializing them into ObservableCollection<UserControl> and I want to put it to my ItemsControl with my own layout.
Now I've created my own UserControl to handle deserialized data to layout and ObservableCollection is binded to my ItemsControl but if I want to display more than 5-10 items my app if getting unresponsive for a while.
How can I avoid freezes? Should I use DataTemplate within ItemsControl or any other ideas? I'm wondering how its done in apps like twitter or reddit which have many entries and everything is working quite nice. Have already searched for reddit/twitter app source to look how they have implemented it, but without success.
EDIT:
xaml
<ScrollViewer>
<ItemsControl x:Name="items" ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
c#
public MainPage()
{
this.InitializeComponent();
items.DataContext = _items;
}
public ObservableCollection<my_item> _items = new ObservableCollection<my_item>();
adding items
for (int i = 0; i < 40; i++)
{
_items.Add(new my_item());
}
my_item is my own UserControl with 2 small images, few buttons and a single TextBlock.
You should use ListView which has a virtualizing panel for displaying items by default:
<ListView ItemsSource={Binding Items} >
<ListView.ItemTemplate>
<DataTemplate>
<!-- here goes your item layout -->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Maybe one reason for your app "hanging" is if you are deserializing/parsing your XML file on the UI thread, especially if the XML file is large, so the solution to this problem is that you need to do the XML parsing on a background thread:
Task.Run(() =>
{
var items = ParseXML();
});
After edit:
1)
Don't wrap an ItemsControl inside a ScrollViewer, because you will break the virtualization. The ItemsControl itself has its own ScrollViewer so there is no need of an additional one.
If you are on Windows Phone 8.1 Runtime, use ItemsStackPanel instead of VirtualizingStackPanel.
2)
It is recommended for lists with many items to use DataTemplates instead of filling it with UI elements. So rather create a data template from your user control:
<DataTemplate>
<MyUserControl/>
</DataTemplate>

xaml / c# stackpanel with background rectangle

I have a list (ListBox) of items in XAML using a StackPanel- based element template. The layout is fine, but I would now like to have a rectangle as a background for each item - creating a box around each one.
I was thinking of using a Canvas somehow, but as each item's height varies (as well as the height of the items inside the StackPanel), I'm not sure how to do it (I'm new to C#/XAML). What would be the best composition for the template in this situation?
You can just specify it in an ItemTemplate and it will do what you want, something like;
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border BorderBrush="Red" BorderThickness="2" Background="Blue"/>
<!-- Insert the rest of your Item template stuff here -->
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
ListBox is a type of ItemsControl, which exposes several properties to control the appearance of the items. In this case, have a look at ItemContainerStyle (in the case of ListBox, the item containers are instances of ListBoxItem). You could, for instance, set the Background property in an ItemsContainerStyle to some color.

Picking the right ItemsSource container

I have a UserControl I've written to display a few properties from a custom object. There are multiple instances of these objects so I have an ObservableCollection of them so I can set them as an ItemsSource binding to a ListView. Now I can get an instance of this UserControl show up for each instance of my class in my ListView.
The problem is I don't really want the behavior of a ListView. I don't want the user to be able to select the entire UserControl. In fact, the user should be able to select individual elements in the UserControl.
I thought about just using a StackPanel to put these UserControls in, but it doesn't have an ItemesSource property. Is there an easy way to make this happen?
Replace your ListView with an ItemsControl and set the ItemTemplate to a suitable DataTemplate for your objects. You can set the ItemsPanel if you want to change how the panel lays out the items.
<ItemsControl ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource ItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
See this example

WPF ListBox not updating with the ItemsSource

I have what I believe should be simple two-way databinding in WPF setup, but the listbox (target) is not updating as the collection changes.
I'm setting this ItemsSource of the ListBox programmatically:
lstVariable_Selected.ItemsSource = m_VariableList;
And the ListBox is declared as follows:
<ListBox Margin="5" Name="lstVariable_Selected">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness="1" Margin="0">
<TextBlock FontSize="25" Text="{Binding Path=Name}" />
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When I initially set the ItemsSource, the ListBox (which is not visible at the time) gets its items set. However, if I go view the ListBox, updates seem to stop at that point.
I can then remove an item from the m_VariableList collection, and it does not disappear from the ListBox. Likewise, if I add one, it doesn't appear.
What gives?
Is your m_VariableList implementing INotifyCollectionChanged? If it's not an ObservableCollection, then changes to it's contents will not automatically be reflected in the UI.
The problem is not in the XAML that you have provided. I used the same XAML successfully in a test application; however, I was able to replicate the issue you are experiencing by re-instantiating the m_VariableList variable.
When the m_VariableList is given a new instance, or pointed to a new object, it is not reflected in the ListBox because the control has its own reference to the data. This may not be the cause of your problem, but I'd recommend looking over your code-behind to ensure that the variable is not getting re-instantiated.
i got stuck for more than hour and then simple logic solved this problem
just set itemsource to clear list and then set source u need again
lstVariable_Selected.ItemsSource = new List<Object>();
lstVariable_Selected.ItemsSource = m_VariableList;

Categories