I have a ListBox in a Windows Phone app. In a button action I need to set a transformation and name on every ListBoxItem in the ListBox called lb.
My datasource is
var items = new ObservableCollection<string>();
for (int i = 0; i < 10; ++i)
{
items.Add("Item " + i);
}
lb.ItemsSource = items;
I have a code to add a RenderTransform to each ListBoxItem in the ListBox
for (int i = 0; i < items.Count;++i )
{
var item = this.lb.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
item.RenderTransform = new CompositeTransform();
item.Name = i.ToString() //needed for storybord
//another stuff
}
and it works ok. The problem is that I first need to insert and item to the list. When I call items.Insert(index,"test") before the for loop I get an exception that the item is null when i==index. It does not matter when I insert the new item, I always get null for that item.
What am I doing wrong? Or is there an event of the ListBox I need to wait for when I insert the new item before trying to acces the ListBoxItem?
Edit: I extracted the code and put it into a solution: https://dl.dropboxusercontent.com/u/73642/PhoneApp2.zip. I first insert a fake item to the new solution, the fade it away and move the original item to that position using an animation.
Right after item added there is not container generated because of asynchronous nature of UI subsystem. Try subscribing on the ItemsChanged (or StatusChanged, sorry i don't remember) and get item when event is fired with proper event args.
Waiting for the Dispatcher to finish doing what its doing such as (updating the UI because of a new item being added)
this.Dispatcher.BeginInvoke(() =>
{
//Code Here
});
If you ever manipulate the UI such as adding an item to a listbox without the UI getting updated, you will not be able to run code targeting the UI.
Edit: Here is the code for your project to get working
private void Button_Click(object sender, RoutedEventArgs e)
{
start = Int32.Parse(from.Text);
end = Int32.Parse(to.Text);
fake = items[start];
//items.Insert(end, fake);
this.Dispatcher.BeginInvoke(() =>
{
for (int i = 0; i < items.Count; ++i)
{
var item = this.lb.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
item.Name = i.ToString();
}
(this.lb.ItemContainerGenerator.ContainerFromIndex(end) as ListBoxItem).RenderTransform = new CompositeTransform();
(this.lb.ItemContainerGenerator.ContainerFromIndex(end) as ListBoxItem).Name = "listBoxItem1";
(this.lb.ItemContainerGenerator.ContainerFromIndex(start) as ListBoxItem).Name = "listBoxItem";
sbListBox.Begin();
});
}
Related
I am working on a program for my boss that creates a display for him to look at on a monitor when he comes to the IT office so he can quickly see what we are working on at a glance (similar to a scrum board). So the 'Task_Board.Models.TaskModel' is a class that I created. What I'm having trouble with is deleting the tasks from the database. I have it working in another part of the code to delete a specific task, but when I try to delete all tasks by putting this logic into a loop then it throws the error above. Here is the method that works for a specific entity:
private void DoneDelete_Click(object sender, RoutedEventArgs e)
{
try
{
var item = (TaskModel)DoneList.SelectedValue;
TaskItem task = NinjectRepo.Instance.TaskItemRepo.Get(item.TaskId);
NinjectRepo.Instance.TaskItemRepo.Remove(task);
Update();
}
catch (Exception a)
{
MessageBox.Show("System Message: " + a.Message, "ERROR");
}
}
And this is the original troublesome code:
for (int i = ToDoList.Items.Count - 1; i >= 0; i--)
{
var item = (TaskModel)ToDoList.Items.GetItemAt(i);
TaskItem task = NinjectRepo.Instance.TaskItemRepo.Get(item.TaskId);
NinjectRepo.Instance.TaskItemRepo.Remove(task);
}
I tried to set the selected value in a different way in the code below, but it throws the same error.
for (int i = ToDoList.Items.Count - 1; i >= 0; i--)
{
ToDoList.SelectedValue = ToDoList.Items.GetItemAt(i);
var item = (TaskModel)ToDoList.SelectedValue;
TaskItem task = NinjectRepo.Instance.TaskItemRepo.Get(item.TaskId);
NinjectRepo.Instance.TaskItemRepo.Remove(task);
}
I don't understand what is happening between the method that works and the one that doesn't because it is basically the exact same logic is it not?
EDIT:
This is how I populate the lists:
items = NinjectRepo.Instance.TaskItemRepo.Get().OrderBy(q=>q.Rank).AsQueryable();
//update ToDo Lists
var tdlist = await items.Select(q => new TaskModel { TaskId = q.TaskId, Status =
q.Status, Rank = q.Rank, Type = q.Type.Name, Assigned = q.Date, Description = q.Description })
.Where(items => items.Status.ToLower() == ToDoStatus.ToLower()).ToListAsync();
ToDoList.ItemsSource = tdlist;
ToDoList.Columns[1].Visibility = Visibility.Hidden;
ToDoList.Columns[4].Visibility = Visibility.Hidden;
I figured out the problem I was having actually was caused by the NewItemPlaceholder that is automatically added at the end of a DataGrid. So when I tried to cycle through the DataGrid's items, when it came to converting the NewItemPlaceholder to a TaskItem it couldn't do it because there was no data in the row. So to fix it I changed the DataGrid to read only in order to get rid of the extra row.
I have a data grid and it is empty at first. Then I need to add items when the user selects some data. But here it shows error like
Error :
System.InvalidOperationException: Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
I really didn't understand what is the problem here.
I have tried datagridName.Items.clear(); and datagridName.Items.Add();. but both did't work.
private void TextboxBarCodeTextchanged(object sender,RoutedEventArgs e)
{
DataGridSalesDetails.Items.Clear();
for (int i = 0; i < AllStockList.Count; i++)
{
if (!string.IsNullOrEmpty((sender as TextBox).Text))
{
if (AllStockList[i].BarCode.StartsWith((sender as TextBox).Text,
StringComparison.InvariantCultureIgnoreCase))
{
Stock vend = AllStockList[i] as Stock;
DataGridSalesDetails.Items.Add(vend);
DataGridSalesDetails.Visibility = Visibility.Visible;
DataGridSalesDetails.Items.Refresh();
TotalReturnAmount = AllStockList.Sum(a => a.TotalAmount);
HiddenTotalAount.Text = TotalReturnAmount.ToString();
LabelFinalAmountValue.Content = TotalReturnAmount.ToString();
}
}
}
}
Expected Result is the Datagrid with added values. What I get is an error. Is there anyone to help me? I'm stuck with my project.
When working with datagrids you should assign a collection of items to the itemSource of that datagrid. Instead of assigning the collection to the itemSource, you are trying to add a new item to that datagrid.
You'll need to filter the Stock objects you are interested in and add these to a collection.
When the iteration is completed and the Stock objects are added to the collection, you'll need to assign the collection to the ItemSource.
Update from OP
The itemssource of the datagrid is bound to an Observable collection.
And OP wants to save the old list of Stock Object Items.
ObservableCollection<Stock> StockItems = new ObserveableCollection<Stock>();
private void TextboxBarCodeTextchanged(object sender,RoutedEventArgs e)
{
oldCollection = ObservableCollection;
for (int i = 0; i < AllStockList.Count; i++)
{
if (!string.IsNullOrEmpty((sender as TextBox).Text))
{
if (AllStockList[i].BarCode.StartsWith((sender as TextBox).Text,
StringComparison.InvariantCultureIgnoreCase))
{
var stock = AllStockList[i] as Stock;
StockItems.Add(stock);
}
}
}
DataGridSalesDetails.Visibility = Visibility.Visible;
TotalReturnAmount = filteredCollection.Sum(a => a.TotalAmount);
HiddenTotalAount.Text = TotalReturnAmount.ToString();
LabelFinalAmountValue.Content = TotalReturnAmount.ToString();
}
I've been developing a WPF application, and I've stumbled upon something interesting, probably because I don't know how it really works underneath.
Basically I'm creating a context menu. The menu has one parent MenuItem, which has three children of the same type. Here's the bare bones code.
public MainWindow()
{
InitializeComponent();
ContextMenu ctxMain = new ContextMenu();
MenuItem parent = CreateMenuItem("Parent", null);
for(int i = 0; i < 3; i++)
{
parent.Items.Add(CreateMenuItem(i.ToString(), () => MessageBox.Show(i.ToString())));
}
ctxMain.Items.Add(parent);
this.ContextMenu = ctxMain;
}
public MenuItem CreateMenuItem(string header, Action action)
{
MenuItem item = new MenuItem();
if (action != null)
{
item.Click += (object sender, RoutedEventArgs e) => action();
}
item.Header = header;
return item;
}
Visually, it's working as expected.
But the action I've given as parameter is acting strange. I'd expect each child item's click to show what's written in their header in the MessageBox. But all of them are displaying '3'.
Am I understanding it correctly, that the inline action I've defined in the for loop, does not get instantiated three times, but only once, using the same parameter 'i'? Or are they three different Action instances, all referring to the same integer? I'd like to hear some clarification to what's going on here.
Thanks in advance.
Regards.
for(int i = 0; i < 3; i++)
{
var localCopy = i;
parent.Items.Add(CreateMenuItem(localCopy.ToString(), () => MessageBox.Show(localCopy.ToString())));
}
Your variable i was "captured" by the lambda expression. But i has changed before your action was executed. And your action executes on the current i, which means 3 (the value i has after the loop and when your action finally executes).
You need to make a local copy of your variable and use that instead. As it has it's own scope inside the loop, it will never change.
I have a method that adds items to my listbox called refreshInterface which is called as soon as the programe starts, adding names of homeforms in the listbox using the FormItems class, here is the rereshInterface method below
public void refreshInterface()
{
//int number = 0;
foreach (DataSet1.xspGetAnalysisUsageTypesRow homeForms in myDataSet.xspGetAnalysisUsageTypes)
{
var forms = new FormItems(homeForms);
listBox1.Items.Add(forms);
}
}
The FormItems class is this below
public class FormItems
{
public DataSet1.xspGetAnalysisUsageTypesRow types { get; set; }
public FormItems(DataSet1.xspGetAnalysisUsageTypesRow usageTypes)
{
types = usageTypes;
}
public override string ToString()
{
// returns the rows that are relating to types.xlib_ID
var libtyps = types.GetxAnalysisUsageRows();
var cnt = 0;
foreach (DataSet1.xAnalysisUsageRow ty in libtyps)
{
//returns true if ty is null
bool typeNull = ty.Isxanu_DefaultNull();
// if its false, if xanu_Default is set
if (!typeNull)
{
cnt += 1;
}
}
var ret = String.Format("set {0} [Set: {1}]", types.xlib_Desc, cnt);
//return this.types.xlib_Desc;
return ret;
}
}
Each listbox (the listbox is on the left of the homeform) item has a number of reports that can be added to it, so for instance, i select an homeform from my listbox, there are 12 textboxes on the right hand side and each textbox has a pair of buttons which are Browse and Clear. If I click on the browse button a new form appears, and i select a report from that form and add it to a particular textbox, the count for that homeform should update, and i clear a textbox for a particular homeform, the count should also update.
At the moment when i debug the application, it shows me the count of each Homeform depending on the amount of reports added to the homeform, but while the programe is running, if i add a new report to a homeform, the count does not update until i restart the debug session. I was told about using a Databinding method but not sure of how i could use it here
How do i ge my listbox item to update ?
You should probably look into binding. Here is a good place to start:
http://www.codeproject.com/Articles/140621/WPF-Tutorial-Concept-Binding
If you want a GUI to respond to data changes then binding is your best friend.
You should bind List Box component source to Observable Collection, every update you do to Observable Collection will update List Box data.
Might not be exact but should give you an idea.
public void refreshInterface()
{
Dictionary<int,string> items = new Dictionary<int,string>();
//int number = 0;
foreach (DataSet1.xspGetAnalysisUsageTypesRow homeForms in myDataSet.xspGetAnalysisUsageTypes)
{
var formitem = new FormItems(homeForms);
items.Add(formitem.someprop, formitem.toString());
}
listbox.DataSource = items;
listbox.DisplayMember = "Value";
listbox.ValueMember = "Key";
}
I'm using the WPF version of TreeView (System.Windows.Controls.TreeView) and need to assign several events. As these are WPF most of the events are either different or behave in a different way hence my confusion as AfterExpand is not available
For now I need 2 events:
SelectedItemChanged which is at TreeView level
ExpandedEvent which is at TreeViewItem level
So far I have
private void DisplayGetEventTypes(UXEvent.GetEventTypesResp resp, CustomAsyncStateContainer state)
{
navBarControl.Groups.Clear();
if (resp.eventTypeItems != null)
{
UXEvent.EventType[] eventItems = resp.eventTypeItems;
int nodeCount = eventItems.Length;
for (int i = 0; i < nodeCount; i++)
{
UXEvent.TryEvent eventItem = new UXEvent.TryEvent();
eventItem.eventName = eventItems[i].name;
eventItem.eventId = eventItems[i].id;
NavBarGroup group1 = new NavBarGroup();
group1.Header = eventItems[i].name;
group1.Tag = eventItem;
group1.IsExpanded = false;
//Add dummy treeview to fill later if expanded
System.Windows.Controls.TreeView treeview = new System.Windows.Controls.TreeView();
treeview.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(myTreeView_SelectedItemChanged);
AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
group1.Content = treeview;
group1.DisplaySource = DisplaySource.Content;
navBarControl.Groups.Add(group1);
}
}
}
and the following two draft event handlers for testing
void myTreeView_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e)
{
// Set the data context of the text block to the selected value.
var node = (ItemsControl)e.OriginalSource;
System.Windows.Forms.MessageBox.Show("Selected!");
}
void myTreeView_ItemExpanded(object sender, RoutedEventArgs e)
{
// Set the data context of the text block to the selected value.
var node = (TreeViewItem)e.OriginalSource;
System.Windows.Forms.MessageBox.Show("Opening! - " + node.Header);
}
The problem I'm having is that myTreeView_ItemExpanded is firing multiple times when any treeviewItem is expanded. I think it fires multiple times due to the event bubbling .
Firstly, can anyone point me to some good resources where I can read up on this behaviour?
Secondly, how can I amend my code to make AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded)); a single event only?
Thank you
I spotted the problem. The line
AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
should read
treeview.AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(myTreeView_ItemExpanded));
I assume the first line must have added the event handler to all available controls and all fired when the event triggered.