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}>
Related
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);
I ve a list from sharepoint and i collect from this list an hyperlink.
As i want my textbox to be like an hyperlink I ve added an event on mousedown to open this hyperlink, My concern is how to collect this hyperlink in the codebehind with the sender.
For the moment I've just hide this hyperlink in the tooltip maybe i can manage this differently any suggestion will be grantly appreciated.
My point so far, i don't know how to get this tooltip in the code behind.
Thanks
My XAML Code :
<ListBox Name="ListboxTips" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=Picture}" Height="20"></Image>
<TextBlock MouseDown="TextBlock_MouseDown_URL" TextDecorations="Underline"
Margin="10,10,20,10" Width="160" TextWrapping="Wrap"
Text="{Binding Path=TitleTip}"
ToolTip="{Binding Path=URL}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My code behind :
foreach (SPSClient.ListItem item in TipsList)
{
var tips = new Tips();
tips.TitleTip = item.FieldValues.Values.ElementAt(1).ToString();
tips.App = item.FieldValues.Values.ElementAt(4).ToString();
// get the Hyperlink field URL value
tips.URL = ((FieldUrlValue)(item["LinkDoc"])).Url.ToString();
//should collect the description of the url
//tips.URLdesc = ((FieldUrlValue)(item["LinkDoc"])).Description.ToString();
tips.Picture = item.FieldValues.Values.ElementAt(4).ToString();
colTips.Add(tips);
}
ListboxTips.DataContext = colTips;
....
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//string test = (ToolTip)(sender as Control).ToString();
System.Diagnostics.Process.Start("http://www.link.com");
//System.Diagnostics.Process.Start(test);
}
Thanks a lot,
You can just access the property directly. It is not elegant, but will work!
private void TextBlock_MouseDown_URL(object sender, MouseButtonEventArgs e)
{
TextBlock txtBlock = sender as TexBlock;
// just access the property
string url = txtBlock.ToolTip as string;
}
A more elegant approach might be to use a Button, Hyperlink or something that exposes a Command, so that you can bind the 'click' action to a command on your view model that performs the action you wish to execute.
usually you stick any data you want to trespass somewhere to Tag attribute.
<TextBlock .. Tag="{Binding Path=URL}" />
This is easily retrievable as a public property:
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tb = sender as TextBlock;
if(tb != null)
{
var neededUrl = tb.Tag;
}
}
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).
How do i access the object UserNames, that is bound to the list??
What i did so far:
Item of the list is object in my case:
new List<UserNames>();
this.users.Add(new UserNames() {Id = 1, UserName = "name 1"});
I am using data template for which i have label and button.
My List is as follows:
<ListBox Grid.Column="1" Grid.Row="1" Name="listBox1" ItemsSource="{Binding}" SelectedValuePath="Id">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Vertical">
<StackPanel>
<Label Content="{Binding UserName}" />
</StackPanel>
<StackPanel Name="ButtonStackPanel">
<Button Name="MyButton" Content="Click Me" Click="MyButton_Click">
</Button>
</StackPanel>
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Where my method for Button is. As you can see i did try to utilise the parent option, but without sucess
private void MyButton_Click(object sender, RoutedEventArgs e)
{
//StackPanel panel = (StackPanel)((Button)sender).Parent;
//WrapPanel wrapPanel = (WrapPanel) panel.Parent;
//ListItem listItem = (ListItem) wrapPanel.Parent;
//ListBox box = (ListBox) listItem.Parent;
//UserNames itemToReport = (UserNames) (box.SelectedItem);
//MessageBox.Show(itemToReport.UserName);
}
You can use the Button's DataContext, since it will be your UserName object
private void MyButton_Click(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
UserNames data = b.DataContext as UserNames;
MessageBox.Show(data.UserName);
}
I've always thought that with WPF, your application is the DataContext, while the UI objects like Buttons, ListBoxes, TextBoxes, etc are simply a pretty layer that sits on top of the DataContext to allow the User to interact with it.
In the XAML, set the Tag property to the current item.
In the click handler, cast it back.
Usernames user = (sender as Button).Tag as Usernames;
To bind a datacollection it is often easiest to use an ObservableCollection (if the data is changing runtime). When you do the binding you have to define a datacontext, a datasoure and a datapath. I will advice you to read some more about binding on MSDN :D
This will work for you -
MessageBox.Show(((sender as Button).DataContext as UserNames).UserName);
I have a Silverlight application that is using a DataGrid. Inside of that DataGrid I have a DataTemplate that is defined like the following:
<Grid x:Name="myGrid" Tag="{Binding}" Loaded="myGrid_Loaded">
<ItemsControl ItemsSource="{Binding MyItems}" Tag="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal" Width="138">
<TextBlock Text="{Binding Type}" />
<TextBox x:Name="myTextBox" TextChanged="myTextBox_TextChanged" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
When a user enters text into the TextBox, I have an event (myTextBox_TextChanged) that must be fired at this point. When that event gets fired, I would like to get the ItemsControl element that is the container for this TextBox. How do I get that ItemsControl from my event handler?
Please note: Because the ItemsControl is in the DataTemplate of DataGrid, I don't believe I can just add an x:Name and reference it from my code-behind. Or is there a way to do that?
Thank you!
Using a combination of ItemsControl.ItemsControlFromItemContainer and VisualTreeHelper.GetParent you should be able to find your ItemsControl
var txt = sender as TextBox;
var panel1 = VisualTreeHelper.GetParent(txt);
var panel2 = VisualTreeHelper.GetParent(panel1);
var contentPresenter = VisualTreeHelper.GetParent(panel2);
var ic = ItemsControl.ItemsControlFromItemContainer(contentPresenter);
You may also want search the web for VisualTreeHelper Recursive functions to make some of this easier.
I like to have this little extension method in a static class somewhere in my app:-
public static IEnumerable<DependencyObject> Ancestors(this DependencyObject root)
{
DependencyObject current = VisualTreeHelper.GetParent(root);
while (current != null)
{
yield return current;
current = VisualTreeHelper.GetParent(current);
}
}
With that you should be able to do something like this:-
ItemsControl control = ((DependencyObject)sender).Ancestors()
.TypeOf<ItemsControl>().FirstOrDefault();
Not sure if this applies but this creates a "toggling button bar" using the same principles.
private void UIClassButton_Click(object sender, RoutedEventArgs e){
Button SenderButton = (Button)sender;
ItemsControl SendersItemControl = ItemsControl.ItemsControlFromItemContainer(VisualTreeHelper.GetParent(SenderButton));
IEnumerable<DependencyObject> DependencyObjectCollection = SendersItemControl.GetContainers();
foreach (ContentPresenter item in DependencyObjectCollection) {
ContentPresenter UIClassPresenter = (ContentPresenter)item;
Button UIClassButton = (Button)UIClassPresenter.GetVisualChildren().First();
if (UIClassButton != SenderButton) {
VisualStateManager.GoToState(UIClassButton, "Normal", true);
}
else {
VisualStateManager.GoToState(UIClassButton, "Pressed", true);
}
}
}
Here's an example of capturing a container that houses your ItemsControl's item:
CheckBox checkbox = sender as CheckBox;
foreach (var item in MembersItemsControl.Items)
{
var container = MembersItemsControl.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
UserInformation user = container.DataContext as UserInformation;
bool isMale = true;
if (user.sex == isMale && checkbox.IsChecked.Value == true)
{
container.Visibility = System.Windows.Visibility.Visible;
}
}
I hope that helps.