How can I position a button img randomly on a grid in XAML? I tried it, but it doesn't work!
This is my code:
public void randomButton()
{
Button newBtn = new Button();
newBtn.Content = "A New Button";
panelButton.Children.Add(newBtn);
Grid.SetRow(newBtn, 1);
Random generator = new Random();
newBtn = generator.Next(1, 100);
}
You need to set the Grid.Row dependency property on the Button.
XAML
<Window x:Class="WpfApplication1.MainWindow" [...] Loaded="Window_Loaded">
<Grid Name="grdMain">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
</Window>
C#
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
void Window_Loaded(object sender, RoutedEventArgs e)
{
//creating the button
Button b = new Button() { Content = "Click me!" };
//when clicked, it'll move to another row
b.Click += (s, ea) => ChangeButtonRow(s as Button);
//adding the button to the grid
grdMain.Children.Add(b);
//calling the row changing method for the 1st time, so the button will appear in a random row
ChangeButtonRow(b);
}
void ChangeButtonRow(Button b)
{
//setting the Grid.Row dep. prop. to a number that's a valid row index
b.SetValue(Grid.RowProperty, new Random().Next(0, grdMain.RowDefinitions.Count));
}
}
}
I hope this helps. :)
Related
as the title says I'm looking for a way to get the Window object the sender is in.
In my situation I have a window in which there is only one button:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<Grid>
<Button Click="Button_Click"/>
</Grid>
</Window>
By clicking the button, it generates a new window without borders and a red rectangle inside it:
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window window = new Window();
window.Height = window.Width = 100;
window.WindowStyle = WindowStyle.None;
Rectangle rectangle = new Rectangle();
rectangle.Height = rectangle.Width = 50;
rectangle.Fill = new SolidColorBrush(Colors.Red);
Grid grid = new Grid();
grid.Children.Add(rectangle);
window.Content = grid;
window.Show();
}
}
}
My idea was to capture the MouseDown event on the rectangle to move the window (using DragMove()), unfortunately I have no idea how to get the Window object on which the rectangle that invoked the event is located, any idea to do it?.
You can use MouseDown event after new Rectangle and find Window through Parent property like this:
private void Rectangle_MouseDown(object sender, MouseButtonEventArgs e)
{
var rect = sender as Rectangle;
FrameworkElement framework = rect;
while (framework != null && (framework as Window)==null && framework.Parent!= null)
{
framework = framework.Parent as FrameworkElement;
}
if(framework is Window window)
{
//here you has found the window
}
}
I created a two thumbs dynamically that look like rectangles and handled the drag and drop events so they can move inside a Canvas. Later when I press some button on the UI, I want to add some strings dynamically to each Thumb inside the Canvas. Is there a way to do it. Please help.
Xaml:
<uwpControls:LayoutTransformControl x:Name="MainLayoutControl" Grid.Row="4" Height="400" Width="600" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid Grid.Row="4" x:Name="gridBarImagePanel" BorderBrush="Black"
BorderThickness="2">
<Image x:Name="BarCodeImage"
RenderTransformOrigin="0.5,0.5"></Image>
<Canvas x:Name="cnvBarCodeImage">
</Canvas>
</Grid>
</uwpControls:LayoutTransformControl>
Code Behind:
private void CreateUIShapes(int numberOfWindows, List<Dimensions> dimensions)
{
Thumb th = null;
for (int i = 0; i < numberOfWindows; i++)
{
th = new Thumb();
th.Name = i.ToString();
var item = dimensions[i];
th.Width = item.Width;
th.Height = item.Height;
th.Foreground = new SolidColorBrush(Windows.UI.Colors.Transparent);
th.BorderBrush = item.BorderColor;
th.BorderThickness = new Thickness(3);
th.DragDelta += (sender, e) => Th_DragDelta(sender, e, dimensions);
th.DragCompleted += (sender, e) => Th_DragCompleted(sender, e, item.IsImageRotated);
//RotateWindowsByAngle(90, th, dimensions, i);
Canvas.SetLeft(th, item.left);
Canvas.SetTop(th, item.Top);
cnvBarCodeImage.Children.Add(th);
}
}
private void BtnScan_Click(object sender, RoutedEventArgs e)
{
//How can I add some text to the Thumb controls at this point. There is no Children Property.
}
Since the Thumb isn't a ContentControl, you will have to supply it with a custom ControlTemplate to add text to it.
Here is a simple example showing how to do this in code:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media;
namespace WpfApp4
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var thumb = new Thumb
{
Width = 100,
Height = 50,
Background = new SolidColorBrush(Colors.Red),
Foreground = new SolidColorBrush(Colors.White),
Template = GetThumbTemplate("")
};
Canvas.SetLeft(thumb, 100);
Canvas.SetTop(thumb, 100);
RootCanvas.Children.Add(thumb);
thumb.Template = GetThumbTemplate("Hello world!");
}
private ControlTemplate GetThumbTemplate(string text)
{
var template = "<ControlTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" TargetType=\"Thumb\">" +
"<Border Background=\"{TemplateBinding Background}\">" +
"<TextBlock VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" Text=\"" + text + "\" />" +
"</Border>" +
"</ControlTemplate>";
return XamlReader.Parse(template) as ControlTemplate;
}
}
}
When I create the Thumb I set the Template with GetThumbTemplate and pass in an empty string. GetThumbTemplate creates a simple ControlTemplate with a Border and a TextBlock. I then parse the XAML and return it.
After I add it to the Canvas, I update the Template of the Thumb using the GetThumbTemplate method, passing in the string I want to display.
I hope this helps!
like some other people here i have a ListView (updated via binding in a GridView).
I want to keep the last inserted Item in the View. So i tried
LView.ScrollIntoView(LView.Items[LView.Items.Count - 1]);
This is working almost fine. Altough the first item which would have to be scrolled into view is only shown like 80% of its whole row (depending on how high i define the whole ListView, i almost got 100%).
The real problem is that the following items which should get scrolled into view are not shown. It is also noticable at the Scrollbar itself which is not at the bottom.
Last Item is not shown
Here is the code of my MainWindow.
public partial class MainWindow : Window
{
private InterfaceCtrl ICtrl;
private ListView LView;
public MainWindow()
{
InitializeComponent();
this.ICtrl = new InterfaceCtrl();
this.ICtrl.ProgressCollection.CollectionChanged += this.CollectionChanged;
Grid MainGrid = new Grid();
this.Content = MainGrid;
GridView gv = new GridView();
Binding StartTimeStampBinding = new Binding() { Path = new PropertyPath("StartTS"), Mode = BindingMode.OneWay, StringFormat = "dd.MM.yyyy - HH:mm:ss.fff" };
GridViewColumn gvTCStartTS = new GridViewColumn() { Header = "Time", Width = 150.00, DisplayMemberBinding = StartTimeStampBinding };
gv.Columns.Add(gvTCStartTS);
LView = new ListView() { Height = 192, Width = 250, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, View = gv, ItemsSource = this.ICtrl.ProgressCollection };
MainGrid.Children.Add(LView);
ICtrl.StartMyThread();
}
private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new System.Action(delegate ()
{
if (LView != null && LView.Items.Count > 0)
{
LView.UpdateLayout();
//LView.ScrollIntoView(LView.Items[LView.Items.Count - 1]);
LView.SelectedIndex = LView.Items.Count;
LView.ScrollIntoView(LView.SelectedItem);
}
}));
}
}
Thank you.
EDIT:
It seemed to be a timing problem, although all the wanted data was in the LView at the right time i tried a workaround with a Textbox bound to the Timestamp.
TextBox tb = new TextBox(); // { Width = 250, Height = 28, Margin= new Thickness(10,100,1,0)};
tb.SetBinding( TextBox.TextProperty , new Binding("LastMsgTimestamp") { Source = this.ICtrl, Mode = BindingMode.OneWay, StringFormat = "dd.MM.yyyy - HH:mm:ss.fff" });
tb.TextChanged += this.UpdateScrollbar;
tb.Visibility = Visibility.Hidden;
It seems to me like there is a timing issue within the binding to the LView and the fired Event of the ObservableCollection. This also includes the PropertyChanged of the ObservableCollection.
I tried the events TargetUpdated and SoruceUpdated directly within LView but those didn't came up at all.
You could try to call any of the ScrollToBottom() or ScrollToVerticalOffset() methods of the ListView's internal ScrollViewer element:
private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new System.Action(delegate ()
{
if (LView != null && LView.Items.Count > 0)
{
LView.UpdateLayout();
ScrollViewer sv = GetChildOfType<ScrollViewer>(LView);
if (sv != null)
sv.ScrollToBottom();
LView.SelectedIndex = LView.Items.Count;
LView.ScrollIntoView(LView.SelectedItem);
}
}));
}
private static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null)
return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null)
return result;
}
return null;
}
I have made the following sample. You could try to call ScrollToBottom in inner ScrollViewer as #mm8 points out. Nevertheless when saw the answer I was already making my sample, so here it is:
Codebehind
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ListViewScroll
{
public partial class MainWindow : Window
{
public ObservableCollection<string> Names { get; set; }
public MainWindow()
{
InitializeComponent();
Names = new ObservableCollection<string>();
ListView.ItemsSource = Names;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Names.Add("Some Name" + ++someInt);
// Get the border of the listview (first child of a listview)
var border = VisualTreeHelper.GetChild(ListView, 0) as Decorator;
// Get scrollviewer
var scrollViewer = border.Child as ScrollViewer;
scrollViewer.ScrollToBottom();
}
private static int someInt;
}
}
XAML
<Window x:Class="ListViewScroll.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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView Grid.Row="0" Name="ListView"/>
<Button Content="Add" FontSize="20" Grid.Row="1"
Click="ButtonBase_OnClick"/>
</Grid>
</Window>
In this case I am handling the scrolling in the button click event but you may change this to fit your requirements
It works, I have tested.
Hope this helps
I am trying to make grid that would contain some number of buttons (1,4,8).
<Window x:Class="WpfApplication9.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">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<!--<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0"></Button>
<Button Grid.Row="0" Grid.Column="1"></Button>
<Button Grid.Row="1" Grid.Column="0"></Button>
<Button Grid.Row="1" Grid.Column="1"></Button>
</Grid>-->
<ItemsControl ItemsSource="{Binding TheList}" x:Name="MyGrid" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="Auto" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding One}">1</Button>
<Button Command="{Binding Four}">4</Button>
<Button Command="{Binding Eight}">8</Button>
</StackPanel>
</Grid>
Code:
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public partial class MainWindow : Window
{
public class ViewModelTest : INotifyPropertyChanged
{
private ObservableCollection<Button> theList;
public ObservableCollection<Button> TheList
{
get { return theList; }
set { theList = value; NotifyPropertyChanged("TheList"); }
}
public ViewModelTest()
{
One = new RelayCommand(() => OneButton());
Four = new RelayCommand(() => FourButton());
Eight = new RelayCommand(() => EightButton());
//TheList.Clear();
//for (int i = 0; i < 4; i++)
//{
// System.Windows.Controls.Button newBtn = new Button();
// newBtn.Content = i.ToString();
// newBtn.Name = "Button" + i.ToString();
// TheList.Add(newBtn);
//}
}
public ICommand One { get; set; }
public ICommand Four { get; set; }
public ICommand Eight { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public void OneButton()
{
TheList = new ObservableCollection<Button>();
for (int i = 0; i < 1; i++)
{
Button btn = new Button();
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
TheList.Add(newBtn);
}
}
public void FourButton()
{
TheList = new ObservableCollection<Button>();
for (int i = 0; i < 4; i++)
{
Button btn = new Button();
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
TheList.Add(newBtn);
}
//MyGrid.Children.Clear();
//MyGrid.ColumnDefinitions.Clear();
//MyGrid.RowDefinitions.Clear();
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//for (int row =0;row<MyGrid.ColumnDefinitions.Count;row++)
//{
// for (int column=0;column<MyGrid.ColumnDefinitions.Count;column++)
// {
// System.Windows.Controls.Button newBtn = new Button();
// newBtn.Content = row.ToString() + column.ToString();
// newBtn.Name = "Button" + row.ToString()+column.ToString();
// newBtn.SetValue(Grid.ColumnProperty,column);
// newBtn.SetValue(Grid.RowProperty, row);
// MyGrid.Children.Add(newBtn);
// }
//}
}
public void EightButton()
{
TheList = new ObservableCollection<Button>();
for (int i = 0; i < 8; i++)
{
Button btn = new Button();
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
TheList.Add(newBtn);
}
//MyGrid.Children.Clear();
//MyGrid.ColumnDefinitions.Clear();
//MyGrid.RowDefinitions.Clear();
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.ColumnDefinitions.Add(new ColumnDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//MyGrid.RowDefinitions.Add(new RowDefinition());
//for (int row = 0; row < MyGrid.ColumnDefinitions.Count; row++)
//{
// for (int column = 0; column < MyGrid.ColumnDefinitions.Count; column++)
// {
// System.Windows.Controls.Button newBtn = new Button();
// newBtn.Content = row.ToString() + column.ToString();
// newBtn.Name = "Button" + row.ToString() + column.ToString();
// newBtn.SetValue(Grid.ColumnProperty, column);
// newBtn.SetValue(Grid.RowProperty, row);
// MyGrid.Children.Add(newBtn);
// }
//}
}
}
public MainWindow()
{
DataContext = new ViewModelTest();
InitializeComponent();
}
}
But I have a problem with displaying. How can I create new columns and rows in itemcontrol dynamically in this case.
Ok, where to begin....
You've got a couple problems here.
TheList is an ObservableCollection, so there is no need for using 'NotifyPropertyChanged' in the TheList property. And no need for the theList member variable. Just use the following for your property:
public ObservableCollection TheList { get; set; }
However, in your code behind, 'TheList' doesn't get initialized until one of your buttons is clicked. Your XAML, therefore is binding to a null object, so pretty much not binding to anything, no matter what you change 'TheList' to after the binding, you're still bound to null. To fix this, make sure to initialize TheList in your constructor before your InitializeComponents() call. Change your constructor to the following:
public MainWindow()
{
TheList = new ObservableCollection<Button>();
DataContext = new ViewModelTest();
InitializeComponent();
}
Also, all the places where you are setting TheList = new ObservableCollection<Button>() just changes the pointer to something else, so the Binding gets broken again. Instead of re-initializing TheList, just do TheList.Clear(); and then add your buttons to TheList using TheList.Add(newBtn);
So, it looks like you have a bunch of RowDefinitions without heights, I'm guessing these are just place holders? You could name your Grid something like x:Name="MyInternalGrid" and then when you're adding to TheList, do the same thing with adding rows to MyInternalGrid. So when call TheList.Clear(); also clear your gridRows by calling MyInternalGrid.RowDefinitions.Clear();. Then, whenever you call TheList.Add(newBtn);, add another row to your grid with MyInternalGrid.RowDefinitions.Add(new RowDefinition{Height=20});. When you do that, make sure to assign the correct row to your newBtn. Grid.SetRow(newBtn, MyInternalGrid.RowDefinitions.Count); before adding the newBtn to TheList. Here's what I mean, change your methods to mimic the refactored EightButton() method below:
public void EightButton()
{
TheList.Clear();
MyInternalGrid.RowDefinitions.Clear();
for (int i = 0; i < 8; i++)
{
Button newBtn = new Button();
newBtn.Content = i.ToString();
newBtn.Name = "Button" + i.ToString();
Grid.SetRow(newBtn, MyInternalGrid.RowDefinitions.Count);
MyInternalGrid.RowDefinitions.Add(new RowDefinition{Height=20});
TheList.Add(newBtn);
}
}
Or, you might just want to not worry about all the grid rows and put a <StackPanel> inside your <Grid> and add all the buttons to that <StackPanel> dynamically. Just clear the stack panel first, MyStackPanel.Children.Clear(); and then in the loop add the new button like this MyStackPanel.Children.Add(newBtn);
Home some of this helps.
i am building an application to show all the software installed in the computer, i already have all the buttons to show with the respective icon, but when i show them, the uniformgrid only shows the buttons that fit in to the window, i thought a scrollbar will show them, but i get to the end of the window and the buttons still missing! how can i show them all with a scrollbar?
Here is the XAML code:
<Window x:Class="apple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid>
<DockPanel Name="dock">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<UniformGrid Name="gridx" DockPanel.Dock="Top" Rows="7" Columns="7">
</UniformGrid>
</ScrollViewer>
</DockPanel>
</Grid>
</Window>
Here is the c# code:
namespace apple
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public string[] link = Directory.GetFiles(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs", "*.lnk", SearchOption.AllDirectories);
public MainWindow()
{
this.ResizeMode = ResizeMode.NoResize;
//this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
InitializeComponent();
masterGUI();
}
public void masterGUI()
{
gridx.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
IconImage[] ico = null;
Bitmap[] img = null;
string[] list = null;
list = new string[link.Length];
ico = new Icon[link.Length];
img = new Bitmap[link.Length];
for (int n = 0; n < link.Length; n++)
{
ImageBrush ib = new ImageBrush();
System.Windows.Controls.Button newBtn = new Button();
list[n] = System.IO.Path.GetFileNameWithoutExtension(link[n]);
FileToImageIconConverter some = new FileToImageIconConverter(link[n]);
ImageSource imgSource = some.Icon;
ib.ImageSource = imgSource;
newBtn.Name = "a" + n;
newBtn.Background = ib;
newBtn.Content = list[n];
newBtn.Click += new RoutedEventHandler(newBtn_Click);
gridx.Children.Add(newBtn);
}
}
private void newBtn_Click(object sender, RoutedEventArgs e)
{
Button clicked = (Button)sender;
string test = null;
test = clicked.Name.Replace("a","0");
this.Close();
System.Diagnostics.Process.Start(link[Int32.Parse(test)]);
}
}
}
Remove the Grid and DockPanel and set either UniformGrid.Rows or UniformGrid.Columns, not both. All you need is Window, ScrollViewer, and UniformGrid:
<Window>
<ScrollViewer>
<UniformGrid Name="gridx" Columns="7"/>
</ScrollViewer>
</Window>
And to do it in a more idiomatic WPF fashion, you should have something like this:
<Window>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Programs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="7"/>
You would then expose a Programs collection from your data source and would thus automatically generate an item for each installed program.