Stackpanel/Itemscontrol Databinding - c#

I have this XAML:
<ItemsControl x:Name="recentSearches"
Margin="0,65,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding q}"
TextWrapping="Wrap"
Foreground="AliceBlue"
Padding="2,6,2,2"
Margin="12,-6,12,0"
FontSize="20" />
</DataTemplate>
</ItemsControl.ItemTemplate>
and this code behind:
private void showLatestSearches()
{
if (fmn.checkLatestSearchesExtistence())
{
List<RecentSearchItem> recent = new List<RecentSearchItem>();
List<String> l = fmn.readLatestSearches();
for (int i = 0; i <= l.Count-1; i += 1)
{
RecentSearchItem r = new RecentSearchItem();
r.q = l[i];
r.generalbg = grau;
recent.Add(r);
}
recentSearches.DataContext = recent;
}
}
the object called fmn reads a .txt from the isolated storage.
But why doesn't anything show up with this StackPanel?

ItemsControl.ItemsSource has to be bound to a collection, for notifications the best would be ObservableCollection<T>.
You are setting the DataContext at the last possible minute, a better way would be to set
DataContext to a ViewModel, could be place where you create your View.
public class Form :UserControl
{
DataContext = new YourViewModel() ;
}
In XAML:
ItemsSource="{Binding SearchesCollection}"
SearchesCollection would be a property in YourViewModel of type ObservableCollection<string>. Whenever you add a new item to SearchesCollection the View updates.
This Databinding Tutorial should help.

Thanks to Lews Therin I managed to finally bind my data to the stackpanel:
<ItemsControl x:Name="recentSearches"
ItemsSource="{Binding recent}"
Background="{Binding generalbg}"
Margin="0,65,0,0" Tap="recentSearches_Tap">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding q}"
Foreground="{Binding foreground}"
TextWrapping="Wrap"
Padding="2,6,2,2"
Margin="12,-6,12,0"
FontSize="20" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and the code behind:
private void showLatestSearches()
{
if (fmn.checkLatestSearchesExtistence())
{
List<RecentSearchItem> recent = new List<RecentSearchItem>();
List<String> l = fmn.readLatestSearches();
for (int i = 0; i <= l.Count-1; i += 1)
{
RecentSearchItem r = new RecentSearchItem();
r.q = l[i];
r.generalbg = grau;
r.foreground = blau;
recent.Add(r);
}
recentSearches.ItemsSource = recent;
}
}
this works, but unfortunately there seems to be no way to determine, which TextBox is tapped, when one is tapped.

Related

Windows Wpf how can I dynamically create xaml grid using c#

Using Visual Studio 2019 + Resharper.
hey guys, i want to add listviews, that show things from objects, which i get from a list.
it looks like this, when i code it manually:
The XAML-Code:
<ListView Margin="43,313,642,29" BorderThickness="2" BorderBrush="Red" x:Name="Module1">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Modul"/>
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and the c# code:
List<Module> somename = pPP_2.Components.Modules.Values.Cast<Module>().ToList();
List<Module> whatevername = new List<Module>(){somename[0]};
Module1.ItemsSource = whatevername;
The Modules i refer to have several properties, and the {somename[0]} just gets the first of them and puts it in the list.
So basically my question:
How can i create such xaml code using c#? I want to create a listview like this for each element in my list. i DonĀ“t want to create them manually but let the code do it for me.
thinking about this for days now and would love to get some help here.
Thanks,
IRezzet.
P.S. You can basically ignore the special list i created there. The question should work for every List.
You could use an ItemsControl with a ItemTemplate that renders a ListView that has an ItemTemplate rendering the listview-items. If this gets too complex, consider seperating this into a usercontrol to make it more generic.
The XAML would look something like this:
<ItemsControl x:Name="DynamicGrid">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ListView BorderThickness="2" ItemsSource="{Binding SubChildren}" BorderBrush="Red">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You will need two Model types for this
// And an instance variable
public ObservableCollection<Outer> Lists { get; } = new ObservableCollection<Outer>();
public class Outer
{
public ObservableCollection<Inner> SubChildren { get; } = new ObservableCollection<Inner>();
}
public class Inner
{
public string Name { get; set; }
}
I used this code to seed for testing:
for (int i = 0; i < 10; i++)
{
var o = new Outer();
for (int k = 0; k < 10; k++)
{
o.SubChildren.Add(new Inner() { Name = "ID: "+k });
}
Lists.Add(o);
}
DynamicGrid.ItemsSource = Lists;

How do I flip this "grid" of displayed objects to be in 3x2 instead of 2x3

I am trying to show the user a bunch of data based on a grid (not the ui element grid). The data changes and the displayed instances are based on the X/Y position of that data. I used an example I found here (but can't find it right now) and I basically got everything working. Except that the displayed grid is on it's side. I have created a test project under which I'm trying to make the thing work correctly before taking that to my main project.
Here is what it looks like
Here is what I want it to look like (Tanks Paint)
Codebehind: (Quick and dirty but works for testing purposes)
private DataContainer[][] dataArray;
public MainWindow()
{
dataArray = new DataContainer[3][];
dataArray[0] = new DataContainer[2];
dataArray[1] = new DataContainer[2];
dataArray[2] = new DataContainer[2];
dataArray[0][0] = new DataContainer(1,"At: 0,0");
dataArray[1][0] = new DataContainer(2, "At: 1,0");
dataArray[2][0] = new DataContainer(3, "At: 2,0");
dataArray[0][1] = new DataContainer(4, "At: 0,1");
dataArray[1][1] = new DataContainer(5, "At: 1,1");
dataArray[2][1] = new DataContainer(6, "At: 2,1");
InitializeComponent();
lst.ItemsSource = dataArray;
}
XAML:
<Window.Resources>
<DataTemplate x:Key="DataTemplate_Level2">
<Border Name="border" BorderBrush="LightGreen" BorderThickness="5"
Padding="2" Margin="2" Width="80">
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Border Name="border2" BorderBrush="Red" BorderThickness="2"
Grid.Row ="0" Padding="1" Margin="1">
<TextBlock Text="{Binding Path=Text1, UpdateSourceTrigger=PropertyChanged}"/>
</Border>
<Border Name="border3" BorderBrush="Blue" BorderThickness="2"
Grid.Row ="1" Padding="1" Margin="1">
<TextBlock Text="{Binding Path=Number1, UpdateSourceTrigger=PropertyChanged}"/>
</Border>
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_Level1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl x:Name="lst" ItemTemplate="{DynamicResource DataTemplate_Level1}"/>
</Grid>
What I've tried:
I tried changing stackpanel orientation to Vertical. That got me half way there. The Result is left column on top of right column. The problem is changing the 3 elements within that column to be Horizontal, which should give the result I'm looking for.
I also triend fondling the DataTemplate_Level2 but that just left me with error messages.
To be honest, I'm new to WPF (in case you haven't noticed) This databinding and templating is the most complicated UI part of my project and I've been thoroughly confused by trying to learn all the stuff at once. I would appreciate it if someone would point me towards an answer here. It's almost Christmas and I would much rather not spend the holidays thinking about this problem.
One solution I can think of is swapping the X and Y coordinates in the array to be dataArray[Y][X] instead of dataArray[X][Y] but that would make things difficult for me in the future and it would be best to get the problems solved now.
In case you wonder what the datacontainer object looks like, but this shouldn't be important to the solution of this issue. it's just something I whipped together to demonstrate that I have gotten the binding and updating to work. It just displays the coordinates it should be at and at random interval changes the value of the number:
public class DataContainer : INotifyPropertyChanged
{
private int number1 = 0;
private string text1 = "none";
private System.Timers.Timer aTimer;
public event PropertyChangedEventHandler PropertyChanged;
public DataContainer(int num,string str)
{
number1 = num;
text1 = str;
aTimer = new System.Timers.Timer(num*1000);
aTimer.Elapsed += OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
Random r = new Random();
number1 = r.Next(0, 100);
number1 += 1;
this.NotifyPropertyChanged("Number1");
}
public int Number1
{
get { return this.number1; }
set
{
if (this.number1 != value)
{
this.number1 = value;
this.NotifyPropertyChanged("Number1");
}
}
}
public string Text1
{
get { return this.text1; }
set
{
if (this.text1 != value)
{
this.text1 = value;
this.NotifyPropertyChanged("Text1");
}
}
}
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public MainWindow()
{
InitializeComponent();
lst.ItemsSource = new List<DataContainer>
{
new DataContainer(1, "At: 0,0"),
new DataContainer(2, "At: 1,0"),
new DataContainer(3, "At: 2,0"),
new DataContainer(4, "At: 0,1"),
new DataContainer(5, "At: 1,1"),
new DataContainer(6, "At: 2,1")
}
}
<ItemsControl x:Name="lst" ItemTemplate="{DynamicResource DataTemplate_Level2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformsGrid Columns="3" Rows="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
I had some sleep and tested out some things and figured out where exactly things are going wrong by adding more borders. Here's the solution that I found myself:
<ItemsControl x:Name="lst" ItemTemplate="{DynamicResource DataTemplate_Level1}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
and the data template:
<DataTemplate x:Key="DataTemplate_Level1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>

Reuse Dynamically created view [duplicate]

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

Binding data in listview itemtemplate using style

I cannot bind my sample data to textblocks in stackpanel, which I defined in resources. I think that I use style in wrong way, because I receive toString() method instead of class binded fields.
That's my resources with defined style:
<UserControl.Resources>
<ItemsPanelTemplate x:Key="VirtualizingStackPanelTemplate">
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
<ListView x:Key="ListBoxTemplate" HorizontalAlignment="Left" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ListView.ItemTemplate>
<DataTemplate>
<!--<ListBoxItem Background="DarkOrchid" Margin="1,1, 5,5" Height="400" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">-->
<StackPanel>
<TextBlock FontSize="30" Text="{Binding Title}"/>
<TextBlock FontSize="20" Text="{Binding Desc}"/>
</StackPanel>
<!--</ListBoxItem>-->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl.Resources>
Here is my method in which i add listview programatically:
long rowCount = ContentGridFullView.RowDefinitions.LongCount();
if (rowCount > 8) return;
var c1 = new RowDefinition { Height = new GridLength(1, GridUnitType.Star) };
ContentGridFullView.RowDefinitions.Add(c1);
rowCount = ContentGridFullView.RowDefinitions.LongCount();
TextBlock tb = new TextBlock {Text = "TEXTBLOCK ITEM = " + (rowCount - 1), FontSize = 40};
Viewbox vb = new Viewbox { Child = tb };
if (rowCount > 8) return;
Grid.SetRow(vb, Convert.ToInt32(rowCount-1));
Grid.SetColumn(vb, 1);
ListView lb = new ListView();
lb.Style = Resources["ListBoxTemplate"] as Style;
lb.ItemsPanel = (ItemsPanelTemplate) Resources["VirtualizingStackPanelTemplate"];
var products = new ObservableCollection<Product>() { new Product("ASDASDSADAS", "VCBVCBVCBVCBC"), new Product("ASDASDSADAS", "VCBVCBVCBVCBC"), new Product("ASDASDSADAS", "VCBVCBVCBVCBC"), new Product("ASDASDSADAS", "VCBVCBVCBVCBC") };
lb.ItemsSource = products;
ContentGridFullView.Children.Add(lb);
ContentGridFullView.Children.Add(vb);
Grid.SetRow(lb, Convert.ToInt32(rowCount - 1));
Grid.SetColumn(lb, 2);
And my short class that I want to bind:
public class Product
{
public string Title { get; set; }
public string Desc { get; set; }
public Product(string title, string desc)
{
Title = title;
Desc = desc;
}
public override string ToString()
{
return "I see that message instead of Title and Desc";
}
}
Can someone tell me what's wrong with this code? Thank you.
Create your Observable collection as a property (getter/setter):
ObservableCollection<Product> _products;
public ObservableCollection<Product> products
{
get{return _products;}
set
{
_products=value;
PropertyChanged("products");
}
}
The property changed event will be need to indicate that the collection has changed,its needed when your using ObservableCollection. You'll need to read more about it.You can add items to the products collection by using :
products.Add(Product_object)
And your xaml code will have the itemsSource as follows:
<ListView x:Key="ListBoxTemplate" HorizontalAlignment="Left" ScrollViewer.HorizontalScrollBarVisibility="Visible" ItemsSource="{Binding products}">
<ListView.ItemTemplate>
<DataTemplate>
<!--<ListBoxItem Background="DarkOrchid" Margin="1,1, 5,5" Height="400" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">-->
<StackPanel>
<TextBlock FontSize="30" Text="{Binding Title}"/>
<TextBlock FontSize="20" Text="{Binding Desc}"/>
</StackPanel>
<!--</ListBoxItem>-->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The following statement is important in your xaml code so that your xaml code will know where to look for the data.
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=x}
First try and create a static list and check if data is getting initialized properly and then you can try creating listviews dynamically. But the code above will be the same thing you will have to do create dynamic listviews.

How to Display Gridview items with variable width in Windows 8?

My GridView items having the size of it's first item size. How do i can change this behaviour ?
How to display GridView items with variable Width as per the content ?
I want to show the first one but i am getting second one. Any suggestion to do that?
Check Windows 8 GridView and Variable-Sized Items and Different Sized Tile Items in WinRT GridView and also check Variable Sized Grid Template Hope this help
You can create such view of GridView by setting ItemsPanel to WrapPanel, you can get WrapPanel on Jerry Nixon's blog. Here's the code.
XAML
<GridView x:Name="gv">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<local:WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="140" Width="{Binding MyWidth}">
<Grid.Background>
<SolidColorBrush Color="{Binding MyColor}" />
</Grid.Background>
<TextBlock FontSize="20" VerticalAlignment="Bottom" Margin="10,0,0,10">
<Run Text="{Binding MyWidth}" />
</TextBlock>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
C#
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var list = new List<ViewModel>()
{
new ViewModel(110, Colors.LawnGreen),
new ViewModel(50, Colors.DarkBlue),
new ViewModel(130, Colors.Firebrick),
new ViewModel(60, Colors.RosyBrown),
new ViewModel(100, Colors.IndianRed),
new ViewModel(210, Colors.BurlyWood),
new ViewModel(150, Colors.Turquoise)
};
gv.ItemsSource = list;
}
public class ViewModel
{
public double MyWidth { get; set; }
public Color MyColor { get; set; }
public ViewModel(double _MyWidth, Color _MyColor)
{
MyWidth = _MyWidth;
MyColor = _MyColor;
}
}
Here is my solution.
//variable sized grid view
public class VariableSizedGridView : GridView
{
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
try
{
dynamic gridItem = item;
var typeItem = item as CommonType;
if (typeItem != null)
{
var heightPecentage = (300.0 / typeItem.WbmImage.PixelHeight);
var itemWidth = typeItem.WbmImage.PixelWidth * heightPecentage;
var columnSpan = Convert.ToInt32(itemWidth / 10.0);
if (gridItem != null)
{
element.SetValue(VariableSizedWrapGrid.ItemWidthProperty, itemWidth);
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, columnSpan);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
}
}
}
catch
{
element.SetValue(VariableSizedWrapGrid.ItemWidthProperty, 100);
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, 1);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
}
finally
{
base.PrepareContainerForItemOverride(element, item);
}
}
//Collection View source
<CollectionViewSource x:Name="collectionViewSource"
Source="{Binding ImageList,Mode=TwoWay}"
IsSourceGrouped="False"
ItemsPath="Items" />
//variable sized Grid view xaml
<controls:VariableSizedGridView x:Name="ImageGridView"
AutomationProperties.AutomationId="ImageGridView"
ItemsSource="{Binding Source={StaticResource collectionViewSource}}"
IsItemClickEnabled="True"
TabIndex="1" >
<controls:VariableSizedGridView.ItemTemplate>
<DataTemplate>
<Grid Height="300" >
<Image Stretch="Uniform" Source="{Binding WbmImage}" />
</Grid>
</DataTemplate>
</controls:VariableSizedGridView.ItemTemplate>
<controls:VariableSizedGridView.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid ItemWidth="10" ItemHeight="300" Orientation="Vertical"/>
</ItemsPanelTemplate>
</controls:VariableSizedGridView.ItemsPanel>
</controls:VariableSizedGridView>

Categories