System.InvalidOperationException when trying yo access UniformGrid.Children in C# - c#

I am populating an UniformGridwith binding. The source is a Square[,], and the UniformGridis filled with Button objects.
Here is what I did :
Data Binding between a double array and a grid
I get a System.InvalidOperationException when trying to do this :
private void OnClickButton(object sender, RoutedEventArgs e)
{
Button b = (Button)sender;
UniformGrid grid = ItemControlGrid.ItemsPanel.LoadContent() as UniformGrid;
int rows = grille.Rows;
int columns = grille.Columns;
UIElementCollection children = grid.Children; // I get the Exception here
int index = children.IndexOf(b);
int row = index / columns;
int column = index % rows;
}
Here is my XAML :
<ItemsControl Background="Gray" Margin="0" Width="800" Height="800"
x:Name="ItemControlGrid"
ItemsSource="{Binding MapGrid}"
ItemTemplateSelector="{StaticResource selector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid
IsItemsHost="true"
x:Name="My_UniformGrid" Rows="25" Columns="25"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Why do I get this Exception?

Change the line where you get an exception to:
ItemCollection children = ItemControlGrid.Items;
The rest of the code should compile just as fine. I assume that grille is actually grid.
<ItemsControl Background="Gray" Margin="0" Width="800" Height="800"
x:Name="ItemControlGrid">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="true" Rows="25" Columns="25"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- First two elements are just dummies to test the result-->
<Control />
<Image />
<Button Click="Button_Click_1" />
</ItemsControl>
Codebehind:
void Button_Click_1(object sender, RoutedEventArgs e)
{
var b = (Button) sender;
var grid = ItemControlGrid.ItemsPanel.LoadContent() as UniformGrid;
var rows = grid.Rows;
var columns = grid.Columns;
var children = ItemControlGrid.Items;
var index = children.IndexOf(b);
var row = index/columns;
var column = index%rows;
//column will be 2
}

Related

Horizontal ListBox multiple lines (WPF)

I want to implement a horizontal ListBox, with multiple lines.
Using WrapPanel:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I get the following result:
But I am looking for this result:
How can I implement this?
Based on your comments, I made a UniformGrid which adjusts the number of rows/columns, whenever the window is resized.
MainWindow.xaml:
<Grid x:Name="Container">
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid x:Name="ItemsGrid" Loaded="ItemsGrid_Loaded" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
The names and events are important. They are used for adjusting the columns/rows in the code file.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private UniformGrid itemsGrid;
public MainWindow()
{
InitializeComponent();
}
private void ItemsGrid_Loaded(object sender, RoutedEventArgs e)
{
// Set reference and adjust columns/rows once the UniformGrid is loaded.
itemsGrid = sender as UniformGrid;
((UniformGrid)sender).Columns = (int)(Container.ActualWidth / 250);
((UniformGrid)sender).Rows = (int)(Container.ActualHeight / 75);
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Adjust number of columns/rows whenevner the window is resized.
if (itemsGrid != null)
{
itemsGrid.Columns = (int)(Container.ActualWidth / 250);
itemsGrid.Rows = (int)(Container.ActualHeight / 75);
}
}
}
This could definitely be cleaned up a bit, but it seems to do the job. There might be more elegant solutions, but I couldn't find any...

Why is data binding to canvas not updating UI

I am trying to data bind a collection of lines, and perform a sort function on them and update the UI once the sort has been completed (would like to show the differences in sort algorithms).
I have a basic WPF application which consists of an ItemsControl which is bound to a collection of objects. These objects are bound correctly when the screen is first rendered, however once the sort operation has been completed, the underlying list has been sorted correctly, but the UI has not been redrawn?
Here is my XAML
<Grid>
<Button Content="Sort" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="12" MinWidth="80" Click="Button_Click"/>
<ItemsControl x:Name="mainControl" ItemsSource="{Binding Values}" ItemTemplate="{StaticResource LineDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter" />
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
there is a xaml data template
<DataTemplate x:Key="LineDataTemplate">
<Line X1="{Binding X1}" Y1="{Binding Y1}"
X2="{Binding X2}" Y2="{Binding Y2}"
Stroke="DarkGray" StrokeThickness="3"/>
</DataTemplate>
The main data context contains a list of this Line object
public class Line
{
public int X1 { get; set; }
public int Y1 { get; set; }
public int X2 { get; set; }
public int Y2 { get; set; }
}
And when the datacontext is initialised I create some random lines
private void RandomiseLines()
{
var rnd = new Random();
var startingPoint = 2;
Values = new List<Line>();
for (int i = 0; i < 3; i++)
{
Values.Add(new Line() { X1 = startingPoint, Y1 = 420, X2 = startingPoint, Y2 = (420 - rnd.Next(1, 300)) });
startingPoint += 4;
}
}
Then I have a button on the UI which calls through and (for now) calls a basic sort using linq
Values = Values.OrderBy(x => x.Y2).ToList();
The data context, where this list is held implements the INotifiedProperty changed interface, and once the list is sorted I make a call to the Property changed event. Although the underlying list get sorted the UI does not seem to be redrawing, I have tried using an ObservableCollection and wrapping in Dispatcher but I do not seem to have any binding errors or exceptions being thrown. Can anyone please explain why this does not get updated?
Edit: Added expected result
The expected result would be the ItemsControl redrawing itself and the lines would be in the new sorted order
You better use Rectangle instead of Line, because it doesn't rely on coordinates for positioning. You just give them a shared Width but a variable Hight. The ItemsPanel should be a StackPanel with the StackPanel.Orientation set to Horizontal. The Value collection must be a ObservableCollection<double>. Then it should behave as expected.
This way the order of the bars will reflect the order of the collection.
The main view
<StackPanel>
<Button Content="Sort"
Click="Button_Click" />
<ItemsControl x:Name="mainControl"
ItemsSource="{Binding Values}"
ItemTemplate="{DynamicResource LineDataTemplate}">
<ItemsControl.Resources>
<DataTemplate x:Key="LineDataTemplate" DataType="system:Double">
<Rectangle Width="5"
Height="{Binding}"
VerticalAlignment="Bottom"
Fill="DarkGray"
Margin="0,0,3,0" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
The Button.Click event handler
private void Button_Click(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as TestViewModel;
var orderedValues = viewModel.Values.OrderBy(value => value).ToList();
viewModel.Values = new ObservableCollection<double>(orderedValues);
}
The view model
private void RandomiseLines()
{
var rnd = new Random();
Values = new ObservableCollection<double>();
for (int i = 0; i < 3; i++)
{
Values.Add(rnd.Next(1, 300));
}
}

change listBox content from MySQL by click

I have a listBox that already populated by MySql table. I want to change the content of listBox anyway user pressing a button, what I done for this is to call sql class creator in my code with new query that filter the data from table, the problem is how to change listbox content in that button event handler?
here is my codes
private void shirtSelect_Click(object sender, RoutedEventArgs e)
{
string shirt = "SELECT * FROM viewermenu.grament where type = 'shirt'";
var shirtTable = new DatabaseTable();
string id = null;
shirtTable.GetTable(shirt, id);
listBox.DataContext = shirtTable;
}
and xaml side:
<ListBox x:Name="listBox" BorderBrush="Transparent" Background="Transparent" SelectionChanged="listBox_SelectionChanged" SelectionMode="Single" ItemsSource="{Binding Source={StaticResource NamesTable}}" it HorizontalContentAlignment="Center" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" RenderTransformOrigin="0.5,0.5" Margin="0" Padding="0,0,0,317" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="200" Width="200" >
<Image Margin="3" Source="{Binding pic_path}" RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased"/>
<TextBox Margin="3" Text="{Binding name}" Visibility="Visible"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and databbase table class:
public DataTable GetTable(String query, String sortBy)
{
String connString = "server=192.168.*.**;uid=*****;pwd=****;database=viewermenu";
connection = new MySqlConnection(connString);
adapter = new MySqlDataAdapter(query, connection);
DataTable dataTable = new DataTable();
adapter.Fill(dataTable);
dataTable.DefaultView.Sort = sortBy;
return dataTable;
}
}
Some mistakes in the given code, correct them and have a try.
1.Add IsItemsHost to the ItemsPanelTemplate:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
2.Define a ShirtData class:
public class ShirtData
{
public string pic_path { get; set; }
public string name { get; set; }
public ShirtData(string path, string theName)
{
pic_path = path;
name = theName;
}
}
3.Set the ItemsSource in the code-behind (some pseudocode):
.......
var table = shirtTable.GetTable(shirt, id);
var shirts = new List<ShirtData>();
foreach (DataRow row in table.Rows)
{
var shirtData = new ShirtData(row["pic_path"].ToString(), row["name"].ToString());
shirts.Add(shirtData);
}
listBox.ItemsSource = shirts;
You need to amend the property that is bound to NamesTable below.
ItemsSource="{Binding Source={StaticResource NamesTable}}"
ie, assuming the property is a list, add and remove items as you would normally do with a list.

Error Dictionary Item 'DataTemplate' must have a Key attribute while set x:DataType="BitmapImage"

I'm trying to implement sample Universal Windows Platform Application for displaying Number of images in a Grid,when i frame images sources into Item-panel then build the solution getting this error "Error Dictionary Item 'Data Template' must have a Key attribute"
Please any one suggest some thing or help me.
public ObservableCollection<BitmapImage> ImgList = new ObservableCollection<BitmapImage>();
for (int i = 1; i < 15; i++)
{
var image = new Image
{
Source = new BitmapImage(
new Uri(
"https://canaryappstorage.blob.core.windows.net/dummy-container/food"+i+"_tn.jpg"))
};
var bitmapImage = new BitmapImage();
ImgList.Add(image.Source as BitmapImage);
image.Source = null;
}
<ItemsControl ItemsSource="{Binding ImgList2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate x:DataType="BitmapImage">
<StackPanel Orientation="Horizontal">
<Image Width="200"
Height="100"
Source="{x:Bind }"
Stretch="UniformToFill" />
</StackPanel>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
You are defining the DataTemplate in ItemsControl.Resources. To make it work as a template for your items, you have to declare it in ItemsControl.ItemTemplate.

Unable to load ListView items using Itemsource

I have a ListView that is enclosed inside a dragablz tabs plugin:
<dragablz:TabablzControl Opacity="0.8" BorderBrush="#FF4C589C" Margin="10,49,10,10" Background="#FF402D61" TabStripPlacement="Left">
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController />
</dragablz:TabablzControl.InterTabController>
<TabItem Header="Texts">
<WrapPanel x:Name="textContainer"/>
</TabItem>
<TabItem Header="Files">
<ListView SelectionMode="Extended" x:Name="files" Background="#FF19174B" AllowDrop="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" PreviewMouseLeftButtonDown="files_PreviewMouseLeftButtonDown" MouseMove="files_MouseMove">
<ListView.DataContext>
<local:FileItem/>
</ListView.DataContext>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel>
<local:FileItem/>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</TabItem>
</dragablz:TabablzControl>
The caller window calls InitItems() method of the window containing this ListView populating it with user controls of type FileItem:
public int InitItems()
{
FileOps.UserDataCollection userData = Utility.userData;
if (userData != null && userData.userData != null && userData.userData.Count > 0)
{
foreach (UserData ud in userData.userData)
{
switch (ud.DataType)
{
case "file": _FileItems.Add(new FileItem(ud));
break;
default: break;
}
}
files.ItemsSource = FileItems;
}
return 0;
}
FileItems and _FileItems are defined as:
private ObservableCollection<FileItem> _FileItems = new ObservableCollection<FileItem>();
public ObservableCollection<FileItem> FileItems
{
get
{
return _FileItems;
}
}
Which are getting data successfully when checked using a breakpoint on line files.ItemsSource = FileItems; in InitItems() method.
But the problem is that the ListView is not showing the user controls. Please tell me what I am missing? I am just a beginner in WPF.

Categories

Resources