how can I bind data to Combobox at runtime? I use template field in Combobox and i try to update Combobox item source in code-behind. but not update xamarin my Combobox in form. and in combobox template field, i want to delete combobox item with a button that event name cbxDeleteStudent_Click. but i can't find comboxitem in code behind.
Please help me.
MyCodes:
<ComboBox x:Name="cbxStudents" Width="150" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel Width="150">
<Label Content="{Binding StudentId}" x:Name="cbxStudentId"></Label>
<Label Content="{Binding StudentName}"></Label>
<Button Content="Sil" x:Name="cbxDeleteStudent" HorizontalAlignment="Right" Width="35"
CommandParameter="{Binding StudentId}" Click="cbxDeleteStudent_Click"></Button>
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code Behind
private void btnAddNewStudent_Click(object sender, RoutedEventArgs e)
{
using (EmployeeDbContext db = new EmployeeDbContext())
{
Student newStudent = new Student()
{
StudentName = txtStudent.Text
};
db.Students.Add(newStudent);
if (db.SaveChanges() > 0)
{
MessageBox.Show(string.Format("{0} öğrencisi başarı ile eklenmiştir.", txtStudent.Text), "Bilgi", MessageBoxButton.OK);
txtStudent.Text = string.Empty;
(cbxStudents.ItemsSource as List<Student>).Add(newStudent);
}
}
}
for delete combobox item
private void cbxDeleteStudent_Click(object sender, RoutedEventArgs e)
{
using (EmployeeDbContext db = new EmployeeDbContext())
{
Student selectedStudent = db.Students.Find(int.Parse((sender as Button).CommandParameter.ToString()));
db.Students.Remove(selectedStudent);
db.SaveChanges();
}
((sender as Button).Parent as DockPanel).Children.Clear();
}
It looks like the the ItemSource used to bind to the ComboBox, is a List<Student>.
Use ObservableCollection(Of T) instead of List<T>, ObservableCollection provides notification when items get added, removed, or when the whole list is refreshed and the ComboBox items are updated, while List<T> doesn't.
Then you just need to add/remove the item from the ObservableCollection, without having to touch the ComboxBox's Items property.
To Add
(cbxStudents.ItemsSource as ObservableCollection<Student>).Add(newStudent);
To Remove
ObservableCollection<Student> students = cbxStudents.ItemsSource as ObservableCollection<Student>;
int studentId = int.Parse((sender as Button).CommandParameter.ToString());
Student selectedStudent = students.SingleOrDefault(s => s.StudentId == studentId);
students.Remove(selectedStudent);
Related
I have a Custom ListBox with multiple columns per one Item
<ListBox Name="UserListBox" Loaded="GetUsers_OnLoad" SelectionChanged="UserSelected">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel Name="UserDockPanel" Margin="4">
<TextBlock Name="UsernameTextBlock" Text="{Binding Path=Username}"/>
<CheckBox Name="OneCheckBox" IsHitTestVisible="False" IsChecked="{Binding One}" />
<CheckBox Name="TwoCheckBox" IsHitTestVisible="False" IsChecked="{Binding Two}" />
<CheckBox Name="ThreeCheckBox" IsHitTestVisible="False" IsChecked="{Binding Three}" />
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
What I am trying to do is when the user selects an item that I can parse the individual values for that item (UsernameTextBlock, OneCheckbox, TwoCheckBox, ThreeCheckBox).
I have tried selected which throws an error and selection changed seems to work but I do not know how to retrieve the individual values for the item selected.
Any insight would be appreciated.
UPDATE:
Here is the code behind
private void UserSelected(object sender, RoutedEventArgs e)
{
var userListBox = FindName("UserListBox") as ListBox;
var selectedItem = userListBox.SelectedItem as ListBoxItem;
MessageBox.Show(selectedItem.Username);
}
I am currently just showing a message popup to show what I am accessing
UPDATE 2:
private void GetUsers_OnLoad(object sender, RoutedEventArgs e)
{
_outreachAuths = _outreachTableAdapter.GetOutreachAuths();
var users = new List<UserItem>();
foreach (DataRow row in _outreachAuths.Rows)
{
users.Add(new UserItem() { Username = row.ItemArray[0].ToString(), One = false, Two = true, Three = ((row.ItemArray[2].ToString() == "1"))});
}
var userList = sender as ListBox;
if (userList != null) userList.ItemsSource = users;
}
In your UserSelected handler you're casting the selected item to type ListBoxItem:
var selectedItem = userListBox.SelectedItem as ListBoxItem;
In order to access the properties you're looking for you'll need to cast it to its original type which is, I believe, UserItem.
var selectedItem = userListBox.SelectedItem as UserItem;
Bind the listbox's SelectedItem property to a property in your view model. You will then have access to the item when it's value changes in the VM.
<ListBox Name="UserListBox" Loaded="GetUsers_OnLoad" SelectionChanged="UserSelected" SelectedItem={Binding Path=PropertyOnViewModel}>
Hi I am trying to loop through rows in a datagrid
If I use:
PagedCollectionView pgView = dataGrid.ItemsSource as PagedCollectionView;
foreach (var item in pgView.)
{}
I get the item as entity and I can't figure out how to cast that to some meaningfull data
can anyone help me there ?
if I use:
IEnumerable list = dataGrid.ItemsSource as IEnumerable;
foreach (var row in list)
{}
I get the same entity and the same problem...
I have looked at the following that accomplishes the task but I feel like I am
mixing data with presentation
PagedCollectionView pgView = verkefniDataGrid.ItemsSource as PagedCollectionView;
foreach (var item in pgView)
{
((CheckBox)verkefniDataGrid.CurrentColumn.GetCellContent(item)).IsChecked = true;
}
Are the any way to get to the data behind the item and set it there to true ?
You need to iterate through the DataGrid's rows, not through the items it is bound to, if you want to check a checkbox control. You would need to loop through the rows and then grab the checkbox in the proper column and set the properties on it.
But as mentioned in the comments, if the checkbox is bound to a property in the data behind the grid, then you should just be able to change that value (as long as the item exposes the INotifyPropertyChanged interface).
Edit Updated link
Using a ViewModel approach, you could define the view like this
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Vertical">
<sdk:DataGrid AutoGenerateColumns="False" Height="151" HorizontalAlignment="Left" Margin="52,67,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="190"
ItemsSource="{Binding Items}">
<sdk:DataGrid.Columns>
<sdk:DataGridCheckBoxColumn Binding="{Binding IsSelected}"/>
<sdk:DataGridTextColumn Binding="{Binding Name}"/>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<Button Content="What is selected?" Width="300" Click="Button_Click"/>
</StackPanel>
</Grid>
Then, you setup the view to be bound against a ViewModel, which contains a property of your PagedCollectionView:
public class ViewModel
{
private PagedCollectionView _items = new PagedCollectionView(
new[]
{new MyItem{Name="Item 1"},
new MyItem{Name="Item 2"},
new MyItem{Name="Item 3"},
new MyItem{Name="Item 4"} });
public PagedCollectionView Items
{
get { return _items; }
}
public string GetSelectedItems()
{
return "Selected items: " +
string.Join(",",
Items.Cast<MyItem>().Where(x => x.IsSelected).
Select(x => x.Name));
}
}
Now, since I don't know your solution that well, I put a piece of ugly code in the code-behind just to show how that the selections are tracked:
private void Button_Click(object sender, RoutedEventArgs e)
{
var viewModel = DataContext as ViewModel;
MessageBox.Show(viewModel.GetSelectedItems());
}
Connecting the View and ViewModel together is done by creating the view, the view model and then stitch them together with the DataContext property of the view.
Like this:
var view = new MyWindow();
var viewModel = new ViewModel();
view.DataContext = viewModel;
That should make the example work.
When I make selection in ComboBox, and then type some text in TextBox, I want to have visible AutoSuggestion list of ID or FirstName or LastName (based on ComboBox Selection) that contains typed string in TextBox. Like this, now it works only for FirstName.
I have problem to somehow set dynamically binding for TextBlock.
Please Help.
Thanks in advance! Marina
I have ComboBox:
<ComboBox Height="23" Name="cbAttrib" Width="120" Margin="0,8,0,0">
<ComboBoxItem>ID</ComboBoxItem>
<ComboBoxItem>FirstName</ComboBoxItem>
<ComboBoxItem>LastName</ComboBoxItem>
</ComboBox>
I have TextBox:
<TextBox Name="txtSearch" TextChanged="txtAutoSuggestName_TextChanged"/>
And this ListBox:
<ListBox Name="listBoxSuggestion" Visibility="Hidden" SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in code I have this methods:
private void txtAutoSuggestName_TextChanged(object sender, TextChangedEventArgs e)
{
listBoxSuggestion.Items.Clear();
if (txtSearch.Text != "")
{
ComboBoxItem cb = (ComboBoxItem)cbAttrib.SelectedItem;
Collection<Person> namelist = proxy.PersonSearch(txtSearch.Text, cb.Content.ToString());
if (namelist.Count > 0)
{
listBoxSuggestion.Visibility = Visibility.Visible;
foreach (var obj in namelist)
{
listBoxSuggestion.Items.Add(obj);
}
}
}
else
{
listBoxSuggestion.Visibility = Visibility.Hidden;
}
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
txtSearch.Text = (e.AddedItems[0] as Person).FirstName.ToString();
listBoxSuggestion.Visibility = System.Windows.Visibility.Hidden;
}
}
You are not binding the Text so nothing will display
You just bind the DataContext, which does nothing if there are no additional bindings which will be relative to it. Just swap that (or add Text="{Binding}" which will bind to the DataContext which is the FirstName) and if your logic is correct it should work.
(Instead of clearing and adding to Items you should just set the ItemsSource instead. listBoxSuggestion.ItemsSource = namelist;)
Edit: To make the binding work for different suggestions change the binding path to Value and make the ItemsSource a collection of some simple objects with a Value property (e.g. use LINQ and anonymous objects).
I am new to MVVM, and also fairly new to WPF. As a matter of fact I started programming just a few months ago. MVVM is really dng my head in with the binding concept, and I have been trying for days now to just simply make an application that allows you to select an item from a listbx, and when you click on the add button the selected item should be saved in a new list. The second listbox displays the latest items added, and you can select an item and delete it by using another button. ususally I would go for the click event and decorate my codebehind with pretty little methods, but I really want to learn how to do all this by using bindings and no codebehind.
I would be extremly happy for any help, and please remember that I am new to this and I really want to keep it as simple as possible :)
with kind regards Daniela
<WrapPanel HorizontalAlignment="Center" Margin=" 10">
<ListBox x:Name="Firstbox"
Width="100"
ItemsSource="{Binding FoodList}"
DisplayMemberPath="Name" >
</ListBox>
<Button Margin="10 >Select</Button>
<ListBox Width="100"></ListBox>
private List _foodList;
public List<FoodItem> FoodList
{
get { return _foodList; }
set { _foodList = value; }
}
private List<FoodItem> _newFoodList;
public List<FoodItem> NewFoodList
{
get { return _newFoodList; }
set { _newFoodList = value; }
}
public MainViewModel()
{
InitializeCommands();
GetFood();
}
private void GetFood()
{
FoodList = new List<FoodItem>()
{
new FoodItem() {Name="Applepie"},
new FoodItem() {Name="Scones"}
};
}
first, you need to replace the Lists with ObservableCollections, so that the UI can detect when new items are added.
Add a SelectedItem property to your ViewModel:
private FoodItem _selectedItem;
public FoodItem SelectedItem
{
get { return _selectedItem;}
set
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
bind the SelectedItem property of the 1st ListBox to this property:
<ListBox Width=" 100" x:Name="Firstbox"
ItemsSource="{Binding FoodList}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedItem}" />
bind your 2nd ListBox to the NewFoodList property
create a command in your ViewModel:
private DelegateCommand _addItemCommand;
public ICommand AddItemCommand
{
get
{
if (_addItemCommand == null)
{
_addItemCommand = new DelegateCommand(AddItem);
}
return _addItemCommand;
}
}
void AddItem()
{
if (SelectedItem != null)
NewFoodList.Add(SelectedItem);
}
And finally, bind the button's Command property to the AddItemCommand property:
<Button Margin="10" Command="{Binding AddItemCommand}" >Select</Button>
I have a listbox with a bunch of contols in each list item.
<ListBox x:Name="projectList" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<ListBox x:Name="taskList" ItemsSource="{Binding Tasks}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="textBoxTask" />
<Button
x:Name="ButtonAddNewTask"
Content="Test"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"
Click="ButtonAddNewTask_Click"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When I click on the button in the listbox i want to add a new item to the listbox within the listbox. I've come this far. So my question is how do I get hold of the textbox and how do I update the listbox?
Here is my click event
private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
Project proj = button.DataContext as Project;
if(proj.Tasks == null)
proj.Tasks = new List<Task>();
proj.Tasks.Add(new Task("Added Task"));
}
Thanx
The easiest solution would likely be to have one object represent each item in the outer ListBox. It would then have properties that would represent each control in the item - the text in the TextBox, and the items in the ListBox (a list of Tasks, I think, based on your Click handler).
In your Click handler, you can get the Button's DataContext (which should be an item in the collection of the outer list), and add a new Task to that object's list of tasks. Since the inner ListBox is bound to that list, it should be updated with the new item (assuming that it sends events when items are added, such as with ObservableCollection).
Update: Based on your comments, the following should work.
Your Project class should have two properties:
class Project
{
public string Name { get; set; }
private ObservableCollection<Task> tasks =
new ObservableCollection<Task>();
public IList<Task> Tasks
{
get { return this.tasks; }
}
}
The Task class just has one property - the name of the task.
The ProjectView class is a wrapper around the Project class (I got this idea from #timothymcgrath's answer). It keeps track of the name of a new task, and the current Project:
class ProjectView : INotifyPropertyChanged
{
public Project Project { get; set; }
private string newTaskName = string.Empty;
public string NewTaskName
{
get { return this.newTaskName; }
set
{
this.newTaskName = value;
this.OnPropertyChanged("NewTaskName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
PropertyChangedEventHandler eh = this.PropertyChanged;
if(null != eh)
{
eh(this, new PropertyChangedEventArgs(propName));
}
}
}
You'll need a new class that will be used as the DataContext. Something like this:
class Model
{
private ObservableCollection<ProjectView> projects =
new ObservableCollection<ProjectView>();
public IList<ProjectView> Projects
{
get { return this.projects; }
}
}
In the code behind, set the DataContext of the object to an instance of the above class:
public class Window1
{
public Window1()
{
this.InitializeComponent();
this.DataContext = this.model;
}
private Model model = new Model();
}
In the XAML, the bindings should be modified to bind to the above properties:
<ListBox x:Name="projectList" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Projects}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Project.Name}" />
<ListBox x:Name="taskList"
ItemsSource="{Binding Project.Tasks}"
DisplayMemberPath="Name" />
<TextBox x:Name="textBoxTask"
Text="{Binding Path=NewTaskName, UpdateSourceTrigger=PropertyChanged}"/>
<Button x:Name="ButtonAddNewTask" Content="Test"
Click="ButtonAddNewTask_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Finally, in the click handler for the button, create the task. The DataContext of the Button will be the ProjectView for that item.
private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
ProjectView curProject = btn.DataContext as Project;
if(null != curProject)
{
curProject.Project.Tasks.Add(new Task()
{
Name = curProject.NewTaskName
});
}
}
Since all of the controls get their values via binding, you don't need to access the control itself to get the data - just use the data structures that are supplying the controls already.
It would probably be better to move the code that creates the Task into another class (possibly Project), but I just left it in the event handler for ease of typing on my part.
Update 2: Modified the above code to move the NewTaskName property into a separate class that wraps an instance of Project for use with the UI. Does this work better for you?
I'm assuming your Project ListBox is populated with an Collection of Project objects. I would add an AddNewTask ICommand to the Project class and expose it through a property. Then bind the Add New Task button to the new AddNewTask ICommand. For the CommandParameter, put the TaskName in and it will be passed into the command.
Try reading up on some MVVM (Model View ViewModel) for some examples of how this works. It is very clean and works great.
This solution worked for the task at hand so to speak.
private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
DependencyObject obj = LogicalTreeHelper.GetParent(button);
StackPanel item = obj as StackPanel;
TextBox textBox = item.FindName("textBoxTask") as TextBox;
ListBox listBox = item.FindName("taskList") as ListBox;
Project proj = button.DataContext as Project;
if(proj.Tasks == null)
proj.Tasks = new List<Task>();
listBox.ItemsSource = proj.Tasks;
listBox.Items.Refresh();
}