i am trying to implement listbox (or listview):
<ListView ItemsSource="{Binding Players}" SelectedIndex="{Binding SelectedIndex}">
My problem is, that i want to bind selected index to property in code-behind.
It work only on form start, but i need to disable user to change selection. Selectin will be changed ONLY programmaticaly.
Thanks for all advices or solutions :)
So, working solution:
private void playersList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender.GetType() == typeof(ListView))
{
(sender as ListView).SelectedIndex = GameObserver.Instance.core.SelectedIndex;
e.Handled = true;
}
}
In XAML:
<ListView ItemsSource="{Binding Players}" SelectedIndex="{Binding SelectedIndex}" SelectionChanged="playersList_SelectionChanged">
And bounded property:
private int selectedIndex = 1;
public int SelectedIndex
{
get
{
return selectedIndex;
}
}
You have two tasks here:
Selecting programmatically: WPF ListView Programmatically Select Item
And disabling user selection: WPF ListView turn off selection
Just have no set
Public Int SelectedIndex
{
get { return selectedindex; }
}
public void mysub()
{
selectedindex = 2;
NotifyPropertyChanged("SelectedIndex");
}
Related
I am having issues with a ComboBoxselecting the first entered character, which then causes the a problem where the second entered character overwrites the first one.
EDIT: A small explanation of what I an trying to do.
I have set up the ComboBox to act as an autocomplete control. When I enter a character, I am using CollectionView class to filter any names that match each entered character. Upon entered text the ComboBox drop down menu needs to open up, which is why I am binding to IsDropDownOpen. This is how it is supposed to look here.
This is beyond me, I can't research what I need to do to stop this behavior.
Here is a screen shot of what I mean.
This is the ComboBox XAML:
<ComboBox Style="{StaticResource ComboBoxToggleHidden}"
DisplayMemberPath="FullName" SelectedValuePath="Key"
IsTextSearchEnabled="False"
IsEditable="True"
StaysOpenOnEdit="True"
Text="{Binding Path=EnteredText, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=Employees}"
SelectedItem="{Binding UpdateSourceTrigger=PropertyChanged, Path=SelectedEmployee}"
IsDropDownOpen="{Binding IsDropDown}">
</ComboBox>
EDIT: I have narrowed it down to this, IsDropDown = true;, commenting this out fixes the issue. But I need the drop down when editing the ComboBox
In the EnteredText property
private string _enteredText;
public string EnteredText
{
get { return _enteredText; }
set
{
_enteredText = value;
Filter(value);
IsDropDown = true;
OnPropertyChanged();
}
}
public bool IsDropDown { get; set; }
OK, I solved this doing a hack, but it will have to do until I can figure out why this behavior is happening.
I created an KeyUpEvent Event in the constructor,
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.KeyUpEvent,
new RoutedEventHandler(DeselectText));
Then in the Handler I just deselected the text.
private void DeselectText(object sender, RoutedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox == null) return;
if (textBox.Text.Length >= 2) return;
textBox.SelectionLength = 0;
textBox.SelectionStart = 1;
}
I know this is a hack, but I have no choice until the correct solution is posted.
This is how it looks with the hack.
Consider this as another solution. It mights resove the side effect of above mentioned problem.
I am expecting the TemplateChild to have the Name (PART_EditableTextBox). If you are changing the name in the Template then please do the necessary changes here as well.
private TextBox EditableTextBox => (TextBox)GetTemplateChild("PART_EditableTextBox");
protected override void OnDropDownOpened(EventArgs e)
{
EditableTextBox.Select(Text.Length, 0);
base.OnDropDownOpened(e);
}
This question already has answers here:
WPF ListView: Changing ItemsSource does not change ListView
(7 answers)
Closed 6 years ago.
I have a little problem and I don't understand where it comes from, I suppose when I will get the answer I will probably said the famous " aaaaaahhhhh yessss ! of course !" so here his my problem : I try to update a listView in mvvm with a drag and drop, with the breakpoints and stuff I can see that the List<string> that goes into the listView is updated and has a new item inside, the element that I pass to the listView Items, but the view doesn't update and the new Item doesn't appear. Here is my code :
private List<string> _listViewItems;
public List<string> listViewItems
{
get
{
return _listViewItems;
}
set
{
_listViewItems = value;
OnPropertyChanged("listViewItems");
}
}
public ICommand MouseMove { get; set; }
//in constructor
MouseMove = new BaseCommand(GoMouseMove);
private void GoMouseMove(object obj)
{
MouseEventArgs e = (MouseEventArgs)obj;
try
{
if (e.LeftButton == MouseButtonState.Pressed)
{
draggedItem = (TreeViewItem) SelectedItem;
if (draggedItem != null)
{
DragDropEffects finalDropEffect = DragDrop.DoDragDrop(SelectedItem, SelectedItem, DragDropEffects.Move);
//Checking target is not null and item is dragging(moving)
if ((finalDropEffect == DragDropEffects.Move))
{
CopyItem(draggedItem, _target);
_target = null;
draggedItem = null;
}
}
}
}
catch (Exception)
{
}
}
private void CopyItem(TreeViewItem _sourceItem, ListViewItem _targetItem)
{
//Asking user wether he want to drop the dragged TreeViewItem here or not
if (MessageBox.Show("Would you like to drop " + _sourceItem.Header.ToString(), "", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
try
{
List<string> items = listViewItems;
items.Add(_sourceItem.Header.ToString());
listViewItems = items;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
When i debug i got that :
<ListView Name="listview1"
Grid.ColumnSpan="2"
Width="auto"
Height="auto"
Grid.Row="1"
AllowDrop="True"
ItemsSource="{Binding listViewItems, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop" >
<cmd:EventToCommand Command="{Binding Drop}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
So we can see that algo.pdf is added, but the view doesn't update. What am I missing ?!
Thank you very much !
A List<> has no way to inform WPF that an item has been added, and therefore WPF can not update the UI to display any added items.
Try using an ObservableCollection<> which will send notificiation events to WPF when you add / remove items.
Also, remember that the items inside your collection should also implement INotifyPropertyChanged if you want WPF to update when properties inside those objects change.
I'm developing a project in c# Wpf with mvvm light.
In this project I have a datagrid with SelectedIndex bound to an int in the ViewModel.
DocumentViewModel:
private int _docSelectedIndex;
public int DocSelectedIndex
{
get { return _docSelectedIndex; }
set
{
_docSelectedIndex = value;
RaisePropertyChanged("DocSelectedIndex");
}
}
The View:
<DataGrid HeadersVisibility="Column"
x:Name="docgrid"
IsSynchronizedWithCurrentItem="True"
DataContext ="{Binding Document, Source={StaticResource Locator}}"
ItemsSource="{Binding Path=DocItems}"
SelectedIndex="{Binding DocSelectedIndex,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
...
...
</DataGrid>
The binding works! but whenever I want to update the Grid with new data I need to RaisePropertyChanged("DocItems").
This seems to change the selectedindex to 0. I've tried to set the selectedindex back to the original, but the index is always set to 0 in the end.
This is the method that calls RaisePropertyChanged:
public void UpdateDocumentList(object sender, TypedEventArg<DocListUpdatedEvent> e)
{
var temp = new List<SFODocument>(e.Value.DocumentList);
var meta = _meta.GetPageMetaData();
foreach (var d in temp)
{
foreach (int i in d.PageList)
{
meta[i].docid = d.DocumentID;
_meta.UpdateExistingMeta(meta[i]);
}
}
_docItems = new ObservableCollection<SFODocument>(temp);
RaisePropertyChanged("DocItems");
}
How can I update the datagrid and still keep the original selectedIndex ?
Try to set the selectedindex back to the original this way
Dispatcher.BeginInvoke(new Action(() => docgrid.SelectedIndex = oldIndex), DispatcherPriority.DataBind);
If it won't work try to use another priorities.
i have an issue with selectedItem of a listbox. When I select an item of the listbox, a popup would be displayed where you click the add button to select an image (it contains a value of selectedItem) which is working fine. But after clicking the add button to select the image, then you realise the image is wrong, so you click the add button again to select another image, it started problem because selectedItem is null. How to handle it? How to stay the value of selectedItem? Your given code much appreciated.
if (lstDinner.SelectedItem != null)
{
output = _imageInserter.InsertImage(imageName, lstDinner.SelectedItem.ToString());
PopupToysImage.IsOpen = true;
strDinner.DinnersDetails = lstDinner.SelectedItem.ToString()
}
else
{
// strDinner.DinnersDetails = null that cause a problem.
output = _imageInserter.InsertImage(imageName, strDinner.DinnersDetails);
PopupDinnerImage.IsOpen = true;
}
UPDATE HERE:
WPF:
<ListBox Style="{DynamicResource ListBoxStyle1}" DisplayMemberPath="Dinner" BorderBrush="#FFF0F0F0" x:Name="lstDinner" FontSize="20" HorizontalAlignment="Left" Margin="0,110,0,72.667" Width="436" SelectionMode="Extended" PreviewMouseLeftButtonDown="MouseDownHandler" ScrollViewer.CanContentScroll="True" UseLayoutRounding="False" KeyDown="lstDinner_KeyDown" MouseDoubleClick="lstDinner_MouseDoubleClick" >
events in C#:
private void MouseDownHandler(object sender, MouseButtonEventArgs e)
{
var parent = (ListBox)sender;
_dragSource = parent;
var data = GetObjectDataFromPoint(parent, e.GetPosition(parent));
if (e.ChangedButton == MouseButton.Left && e.ClickCount == 1)
{
if (data != null)
DragDrop.DoDragDrop(parent, data, DragDropEffects.Move);
}
}
private void lstDinner_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
RemoveItemsFromDatabase();
}
}
private void lstDinner_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_dinnerImage = new DinnerImageExtractor();
BitmapImage getImage = new BitmapImage();
if (lstDinner.SelectedItem != null)
{
getImage = _dinnerImage.GetDinnerImages(lstDinner.SelectedItem.ToString());
if (getImage != null)
{
DinnerImagePopup.Source = getImage;
}
else
{
DinnerImagePopup.Source = new BitmapImage(new Uri("/DinnerApplicationWPF;component/Menu/Images/noImage-icon-pink.png", UriKind.Relative));
}
PopupDinnerImage.IsOpen = true;
// PopupInstrcution.IsOpen = false;
}
}
I would suggest something like this
if ( lstDinner.SelectedItem == null)
{
output = _imageInserter.InsertImage(imageName, lstToys.SelectedItem.ToString());
PopupToysImage.IsOpen = true;
lstDinner.Databind();
}
Note: This may not work as I dont have your actual code. I have added DataBind() in the if statement, if the selected item was null. It should refresh the list.
Best thing is to use two different Listbox item templates for selected and unselected items. So without displaying popup, you can add button into the selected item template.
Are you disabling the ListBox while you select the image?
If so I believe by simply disabling the ListBox the SelectedItem will be set to null.
EDIT:
I imagine you want your event handlers (like the mouse double click) to happen when an item in your list is double clicked, not when the ListBox is double clicked. You need to change your XAML to this:
<ListBox Style="{DynamicResource ListBoxStyle1}" DisplayMemberPath="Dinner" BorderBrush="#FFF0F0F0" x:Name="lstDinner" FontSize="20" HorizontalAlignment="Left" Margin="0,110,0,72.667" Width="436" SelectionMode="Extended" PreviewMouseLeftButtonDown="MouseDownHandler" ScrollViewer.CanContentScroll="True" UseLayoutRounding="False" KeyDown="lstDinner_KeyDown">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<EventSetter Event="MouseDoubleClick" Handler="lstDinner_MouseDoubleClick" />
</Style>
</ListBox.Resources>
</ListBox>
My selected item does not come up null when I run this code.
Short version
I would like to scroll the ListBox item into view when the selection is changed.
Long version
I have a ListBox with the ItemsSource bound to a CollectionViewSource with a GroupDescription, as per the example below.
<Window.Resources>
<CollectionViewSource x:Key="AnimalsView" Source="{Binding Source={StaticResource Animals}, Path=AnimalList}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<ListBox x:Name="AnimalsListBox"ItemsSource="{Binding Source={StaticResource AnimalsView}}" ItemTemplate="{StaticResource AnimalTemplate}" SelectionChanged="ListBox_SelectionChanged">
<ListBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource CategoryTemplate}" />
</ListBox.GroupStyle>
</ListBox>
There is a SelectionChanged event in the a code-behind file.
public List<Animal> Animals { get; set; }
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox control = (ListBox)sender;
control.ScrollIntoView(control.SelectedItem);
}
Now. If I set the AnimalsListBox.SelectedItem to an item that is currently not visible I would like to have it scroll in view. This is where it gets tricky, as the ListBox is being groups (the IsGrouped property is true) the call to ScrollIntoView fails.
System.Windows.Controls.ListBox via Reflector. Note the base.IsGrouping in the OnBringItemIntoView.
public void ScrollIntoView(object item)
{
if (base.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
this.OnBringItemIntoView(item);
}
else
{
base.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnBringItemIntoView), item);
}
}
private object OnBringItemIntoView(object arg)
{
FrameworkElement element = base.ItemContainerGenerator.ContainerFromItem(arg) as FrameworkElement;
if (element != null)
{
element.BringIntoView();
}
else if (!base.IsGrouping && base.Items.Contains(arg))
{
VirtualizingPanel itemsHost = base.ItemsHost as VirtualizingPanel;
if (itemsHost != null)
{
itemsHost.BringIndexIntoView(base.Items.IndexOf(arg));
}
}
return null;
}
Questions
Can anyone explain why it does not work when using grouping?
The ItemContainerGenerator.ContainerFromItem always returns null, even though it's status states that all the containers have been generated.
How I can achieve the scrolling into view when using grouping?
I have found a solution to my problem. I was certain that I wasn't the first person to hit this issue so I continued to search StackOverflow for solutions and I stumbled upon this answer by David about how ItemContainerGenerator works with a grouped list.
David's solution was to delay accessing the ItemContainerGenerator until after the rendering process.
I have implemented this solution, with a few changes that I will detail after.
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox control = (ListBox)sender;
if (control.IsGrouping)
{
if (control.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView));
else
control.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
else
control.ScrollIntoView(control.SelectedItem);
}
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return;
ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView));
}
private void DelayedBringIntoView()
{
var item = ItemContainerGenerator.ContainerFromItem(SelectedItem) as ListBoxItem;
if (item != null)
item.BringIntoView();
}
Changes:
Only uses the ItemContainerGenerator approach when it IsGrouping is true, otherwise continue to use the default ScrollIntoView.
Check if the ItemContainerGenerator is ready, if so dispatch the action, otherwise listen for the ItemContainerGenerator status to change.. This is important as if it is ready then the StatusChanged event will never fire.
The out of the box VirtualizingStackPanel does not support virtualizing grouped collection views. When a grouped collection is rendered in an ItemsControl, each group as a whole is an item as opposed to each item in the collection which results in "jerky" scrolling to each group header and not each item.
You'll probably need to roll your own VirtualizingStackPanel or ItemContainerGenerator in order to keep track of the containers displayed in a group. It sounds ridiculous, but the default virtualization with grouping in WPF is lacking to say the least.