I'm having some trouble with Drag and Drop between different ListViews. I have a GridView that contains multiple ListView all from the same ObservableCollection. Basically, I want to be able to drag items from one ListView into another Listview but within the same GridView.
Below is the ListView Which is part of the DataTemplate for the GridView. The ListView is Bound to a ObservableCollection of string. Which is a Property or the ObservableCollection the GridView is bound too.
<ListView Name="ListingView" Height="200" HorizontalAlignment="Center" Grid.Row="1" CanDragItems="True" AllowDrop="True"
DragItemsStarting="ListView_DragItemsStarting"
DragOver="ListView_DragOver"
CanReorderItems="True"
DragItemsCompleted="ListView_DragItemsCompleted"
Drop="ListView_Drop" ItemsSource="{Binding Listing}"
/>
And here is the Code Behind for the ListView
private void ListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
var item = string.Join(",", e.Items.Cast<string>());
e.Data.SetText(item);
e.Data.RequestedOperation = DataPackageOperation.Move;
}
private void ListView_DragOver(object sender, DragEventArgs e)
{
if (e.DataView.Contains(StandardDataFormats.Text))
{
e.AcceptedOperation = DataPackageOperation.Move;
}
}
private async void ListView_Drop(object sender, DragEventArgs e)
{
if (e.DataView.Contains(StandardDataFormats.Text))
{
var item = await e.DataView.GetTextAsync();
var destinationListView = sender as ListView;
var listViewItemsSource = destinationListView?.ItemsSource as ObservableCollection<string>;
if (listViewItemsSource != null)
{
listViewItemsSource.Add(item);
}
}
}
private void ListView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
{
var item = string.Join(",", args.Items.Cast<string>());
var destinationListView = sender as ListView;
var listViewItemsSource = destinationListView?.ItemsSource as ObservableCollection<string>;
listViewItemsSource.Remove(item);
}
}
The Problem I have is finding a way to stop a ListView from becoming completely empty if I drag all the items out of the a ListView. Once the ListView becomes empty I can't go back and drag something into it.
The Second problem is when I drag an item and then let go of it in the same ListView it was dragged from then it deletes and does no re-add the item.
I think I need to add an If statement to the DragItemCompleted method to see If the source is the same as the target before removing the item.
Edit I seem to have fixed" the second problem by removing the CanReorderItemsProperty from the listView. I'm not sure why but it seems to have fixed up that issue. But now i can't reorder the lists :(
Look into this windows sample program https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlDragAndDrop/cs
I subscribed to a SelectionChangedEvent on a ComboBox in a DataGrid with the following code:
public static DataGridTemplateColumn CreateComboboxColumn(string colName, Binding textBinding, SelectionChangedEventHandler selChangedHandler = null)
{
var cboColumn = new DataGridTemplateColumn {Header = colName};
...
if (selChangedHandler != null)
cboFactory.AddHandler(Selector.SelectionChangedEvent, selChangedHandler);
...
return cboColumn;
}
The handler I actually register contains:
private void ComboBoxSelectionChangedHandler(object sender, SelectionChangedEventArgs e)
{
Console.WriteLine(#"selectHandler");
var cboBox = sender as ComboBox;
if (cboBox == null)
return;
if (cboBox.IsDropDownOpen) // a selection in combobox was made
{
CommitEdit();
}
else // trigger the combobox to show its list
cboBox.IsDropDownOpen = true;
}
... and is located in my custom DataGrid class.
If I select an item in the ComboBox, e.AddedItems and cboBox.SelectedItem contains the selected value, but nothing is changed on CommitEdit().
What I want is to force a commit to directly update the DataGrid's ItemsSource, when the user selects an item in the drop-down-list. Normally this is raised if the control looses focus...
The link in the solution found in this thread is not available any more and I don't know how to use this code.
I created a tricky, but working, solution for my problem. Here's the modified handler from above:
private void ComboBoxSelectionChangedHandler(object sender, SelectionChangedEventArgs e)
{
Console.WriteLine(#"selectHandler");
var cboBox = sender as ComboBox;
if (cboBox == null)
return;
if (cboBox.IsDropDownOpen) // a selection in combobox was made
{
cboBox.Text = cboBox.SelectedValue as string;
cboBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
}
else // user wants to open the combobox
cboBox.IsDropDownOpen = true;
}
Because my ComboBoxColumn is a custom DataGridTemplateColumn I force it to show its list, when the user first selects the cell.
To change the bound items value I manually overwrite the displayed text with recently selected item and force the UI to select another item (in this case the control to the right) to make an implicit call to CellEditEnding event, which (in my case) commits the whole row:
private bool _isManualEditCommit = false;
private void _CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
// commit a manual edit
// this if-clause prevents double execution of the EditEnding event
if (!_isManualEditCommit)
{
Console.WriteLine(#"_CellEditEnding() manualeditcommit");
_isManualEditCommit = true;
CommitEdit(DataGridEditingUnit.Row, true);
_isManualEditCommit = false;
checkRow(e.Row);
}
}
Maybe I could help somebody with this "dirty" solution ;-)
I need to retrieve components from a listbox item. Not from a Tap or selection changed events. Is there a simple way to achieve this?
If I remember correct, in Android you can just write:
layoutRoot.findViewById(R.id.name);
Is there a similar way to do this on Windows Phone?
Update: Here's what I tried so far, but does not work:
What Daniela said in option 5 seems to work when I have the ListBoxItem. So this works fine when I have for example a Tap event on the ListBoxItem:
private void ListBoxItem_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
ListBoxItem item = sender as ListBoxItem;
// I can now retrieve a component inside the ListBoxItem
TextBox textBox = item.FindName("myTextBox") as TextBox;
}
But I want to do this when not triggering any events from a ListBoxItem.
What JoonasL said looks like something I could use but I can't get it to work.
// will not compile ( Cannot implicitly convert type 'object' to... )
ListBoxItem item = x.Items[0];
// if cast the value will be null
ListBoxItem item = x.Items[0] as ListBoxItem;
// will return the object used to populate that ListBoxItem, not the actual ListBoxItem.
// item will have a ItemViewModel object.
List<ItemViewModel> list = ....
this.myListBox.ItemsSource = list;
var item = x.Items[0]
When searching on Google I found something that I could use to find a component inside a ListBoxItem but I think there should be a easier way. Here they use a VisualTreeHelper.
Edit:
add a name to your lisbox (x:Name="something")
Cast ALL items as a List or pick out an item and cast the item as the correct type. Example:
private void Button_Click(object sender, RoutedEventArgs e)
{
var list = yourListbox.Items;
var itemCastAsCorrectObjectInstance = (ItemViewModel)list.FirstOrDefault();
textblock.Text = itemCastAsCorrectObjectInstance.LineOne;
}
ItemViewModel is a class we have created, and a list of ItemViewModels are used as itemssource for the listbox.
Here is an app example I've made for you
There are several ways to do that (some of the examples are WPF, but code is quite similar, it's just to give you a general idea)
Create listbox in code and retrieve item as in the example provided my JoonasL
private void GetUserRecommendations()
{
var obj = _helper.GetList<Recommendations>(#"http://localhost:1613/Home/GetAllRecommendations");
_items.Clear();
foreach (var item in obj)
{
_items.Add(item);
}
itemListView.ItemsSource = _items;
}
Retrieve a selected item on a changed event (or other event bound to the listbox)
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
var itemProperty = ((ListBoxItem) e.ClickedItem).SomeProperty;
}
Provide the listbox a name and access the items by refering to the name in the code
var item = itemListView.SelectedItem as SomeClass;
Access the selected item by binding to another element (XAML only)
<Border Margin="10" BorderBrush="Silver" BorderThickness="3" Padding="8">
<DockPanel>
<TextBlock>Choose a Color:</TextBlock>
<ComboBox Name="myComboBox" SelectedIndex="0">
<ComboBoxItem>Green</ComboBoxItem>
<ComboBoxItem>Blue</ComboBoxItem>
<ComboBoxItem>Red</ComboBoxItem>
</ComboBox>
<Canvas>
<Canvas.Background>
<Binding ElementName="myComboBox" Path="SelectedItem.Content"/>
</Canvas.Background>
</Canvas>
</DockPanel>
Search the layoutroot
var myTextBlock = (TextBlock)this.FindName("myTextBlock");
Or maybe something like:
private void Somepage_Loaded(object sender, RoutedEventArgs e)
{
if (SomeCondition())
{
var children = (sender as Panel).Children;
var child = (from Control child in children
where child.Name == "NameTextBox"
select child).First();
child.Focus();
}
ListBox x = new ListBox();
ListBoxItem item = x.Items[0];
Or do you want something else?
RadDataBoundListBox has an event called ItemTap (not sure about normal ListBox) but my solution went something like this:
private void FocusTextBox(object sender, ListBoxItemTapEventArgs e)
{
txtBox = e.Item.FindChildByType<TextBox>();
var item = itemListView.SelectedItem as PanoramaApp1.//Your Selected Item Type Here;
//Do Something With item Here
itemListView.SelectedItem = null;
}
How do I retrieve the Tag from a listbox item? I have built a list by pulling values from a file and generated textboxes with the parsed data then I made the textboxes a chile of the border. Then I add the border to the listboxitem. So I want to add a tag with a string value and then retrieve that invisible value using the selected item.
So I set the tag...
//created a border above
ListBoxItem item = new ListBoxItem();
item.Tag = path;
item.Content = myBorder;
listBox.Items.Add(item);
Now when that item is selected and I want to read that tag how could I do that?
Since you mentioned that you are using the text-box you could try something like this
if(lb.SelectedItem != -1)
{
string selectedTagx = ((TextBox)lb.SelectedItem).Tag.ToString();
//if just a listbox item
string selectedTagx = ((ListBoxItem)lb.SelectedItem).Tag.ToString();
}
you can add the following to your window or usercontrol that holds the listbox
public MainWindow()
{
InitializeComponent();
//created a border above
ListBoxItem item = new ListBoxItem();
item.Tag = path;
item.Content = myBorder;
listBox.Items.Add(item);
listBox.SelectionChanged += new SelectionChangedEventHandler(listBox_SelectionChanged);
}
void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string path = (listBox.SelectedItem as ListBoxItem).Tag as string;
}
Where "MainWindow()" is the constructor of your window or usercontrol
You can also add the event handler in xaml instead of in the constructor
<ListBox Height="100" Name="listBox" Width="120"
SelectionChanged="listBox_SelectionChanged"/>
C#, .NET 4.0, VS2010.
New to WPF. I have a ComboBox on my MainWindow. I hooked the SelectionChanged event of said combo box. However, if I examine the value of the combo box in the event handler, it has the old value. This sounds more like a "SelectionChanging" event, than a SelectionChanged event.
How do I get the new value of the ComboBox after the selection has actually happend?
Currently:
this.MyComboBox.SelectionChanged += new SelectionChangedEventHandler(OnMyComboBoxChanged);
...
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = this.MyComboBox.Text;
}
Note, I get the same behaviour if I use the object being passed in the event args, e.g. e.OriginalSource.
According to MSDN, e.AddedItems:
Gets a list that contains the items that were selected.
So you could use:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = (e.AddedItems[0] as ComboBoxItem).Content as string;
}
You could also use SelectedItem if you use string values for the Items from the sender:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = (sender as ComboBox).SelectedItem as string;
}
or
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
}
Since both Content and SelectedItem are objects, a safer approach would be to use .ToString() instead of as string
The correct value to check here is the SelectedItem property.
A ComboBox is a composite control with two of its parts being:
The Text Part: the value in the this part corresponds to the Text property of the ComboBox.
The Selector Part (i.e. the "drop-down" part): The selected item in this part corresponds to the SelectedItem property.
The image above was taken immediately after the ComboBox was expanded (i.e. before selecting a new value). At this point both Text and SelectedItem are "Info", assuming the ComboBox items were strings. If the ComboBox items were instead all the values of an Enum called "LogLevel", SelectedItem would currently be LogLevel.Info.
When an item in the drop-down is clicked on, the value of SelectedItem is changed and the SelectionChanged event is raised. The Text property isn't updated yet, though, as the Text Part isn't updated until after the SelectionChanged handler is finished. This can be observed by putting a breakpoint in the handler and looking at the control:
Since the Text Part hasn't been updated at this point, the Text property returns the previously selected value.
Use the DropDownClosed event instead of selectionChanged if you want the current value of the combo box.
private void comboBox_DropDownClosed(object sender, EventArgs e)
{
MessageBox.Show(comboBox.Text)
}
Is really that simple.
This worked for me:
private void AppName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem cbi = (ComboBoxItem)AppName.SelectedItem;
string selectedText = cbi.Content.ToString();
}
This worked for me:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
var text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
}
Following event is fired for any change of the text in the ComboBox (when the selected index is changed and when the text is changed by editing too).
<ComboBox IsEditable="True" TextBoxBase.TextChanged="cbx_TextChanged" />
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string newItem = ((DataRowView) e.AddedItems[0]).Row.ItemArray[0].ToString();
}
The second option didn't work for me because the .Text element was out of scope (C# 4.0 VS2008). This was my solution...
string test = null;
foreach (ComboBoxItem item in e.AddedItems)
{
test = item.Content.ToString();
break;
}
If you really need the SelectionChanged event, then the best answer is SwDevMan81's answer. However, if you are starting with WPF, then you might want to learn how to do things the WPF way, which is different than the old Windows Forms days that used to rely on events like SelectionChanged, with WPF and Model View ViewModel pattern, you should use bindings. Here is a code example:
// In the Views folder: /Views/MyWindow.xaml:
// ...
<ComboBox ItemsSource="{Binding MyViewModel.MyProperties, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding MyViewModel.MyProperty , RelativeSource={RelativeSource AncestorType=Window}}" />
// ...
// In the Views folder: /Views/MyWindow.xaml.cs:
public partial class MyWindow : Window
{
public MyViewModelClass MyViewModel {
get { return _viewModel; }
private set { _viewModel = value;}
}
public MyWindow()
{
MyViewModel.PropertyChanged += MyViewModel_PropertyChanged;
}
void MyViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "MyProperty")
{
// Do Work
// Put your logic here!
}
}
}
using System.ComponentModel;
// In your ViewModel folder: /ViewModels/MyViewModelClass.cs:
public class MyViewModelClass : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation:
private void NotifyPropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
public event PropertyChangedEventHandler PropertyChanged;
// Selected option:
private string _myProperty;
public string MyProperty {
get { return _myProperty; }
set { _myProperty = value; NotifyPropertyChanged("MyProperty"); }
}
// Available options:
private List<string> _myProperties;
public List<string> MyProperties {
get { return _myProperties; }
set { _myProperties = value; NotifyPropertyChanged("MyProperties"); }
}
}
private void indBoxProject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int NewProjID = (e.AddedItems[0] as kProject).ProjectID;
this.MyProject = new kProject(NewProjID);
LoadWorkPhase();
}
The use of the e.AddedItems[0] as kProject where kProject is a class that holds the data worked for me as it was defaulting to the RemovedItems[0] before I have made this explicit distinction. Thanks SwDevMan81 for the initial information that answered this question for me.
I needed to solve this in VB.NET. Here's what I've got that seems to work:
Private Sub ComboBox1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles ComboBox_AllSites.SelectionChanged
Dim cr As System.Windows.Controls.ComboBoxItem = ComboBox1.SelectedValue
Dim currentText = cr.Content
MessageBox.Show(currentText)
End Sub
It's weird that SelectedItem holds the fresh data, whereas SelectedValue doesn't. Sounds like a bug to me. If your items in the Combobox are objects other than ComboBoxItems, you will need something like this: (my ComboBox contains KeyValuePairs)
var selectedItem = (KeyValuePair<string, string>?)(sender as ComboBox).SelectedItem;
if (!selectedItem.HasValue)
return;
string selectedValue = selectedItem.Value.Value; // first .Value gets ref to KVPair
ComboBox.SelectedItem can be null, whereas Visual Studio keeps telling me that a KeyValuePair can't be null. That's why I cast the SelectedItem to a nullable KeyValuePair<string, string>?. Then I check if selectedItem has a value other than null. This approach should be applicable to whatever type your selected item actually is.
Don't complicate things for no reason. Using SelectedValue property you can easily get a selected ComboBox value like this: YourComboBoxName.SelectedValue.ToString().
Behind the scene the SelectedValue property is defined as: SelectedValue{get; set;} this means you can use it to get or set the value of a ComboBox.
Using SelectedItem is not an efficient way to get a ComboBox value since it requires a lot of ramifications.
You can check SelectedIndex or SelectedValue or SelectedItem property in the SelectionChanged event of the Combobox control.
From SelectionChanged event of a combobox you can get the selected item text as follow:
private void myComboBox_SelectionChanged (object sender, SelectionChangedEventArgs e)
{
ComboBoxItem comboBoxItem = (ComboBoxItem) e.AddedItems[0];
string selectedItemText = comboBoxItem.Content.ToString();
}
clean and easy - TextChanged
private void ComboBox1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show(ComboBox1.Text);
}
Following works for me to get current selection of ComboBox (c# / .net6):
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
string? text = ((ComboBoxItem)(sender as ComboBox).SelectedItem).Content as string;
if(text != null){
...
}
}
This should work for you ...
int myInt= ((data)(((object[])(e.AddedItems))[0])).kid;
I solved this by using the DropDownClosed event because this fires slightly after the value is changed.