WPF ListBox SelectedItem issue - wpf

Can anyone help, lost a lot of time trying to figure this issue out.
I have a ViewModel with following properties
public ObservableCollection<ListItem> OpcoList { get; set; }
public ListItem SelectedOpco {
get {
return _selectedOpco;
}
set {
if (_selectedOpco != null && _selectedOpco.Equals(value)) {
return;
}
var oldValue = _selectedOpco;
_selectedOpco = value;
RaisePropertyChanged("SelectedOpco");
}
And in my XAML i have
<ListBox Name="listOpco" ItemsSource="{Binding OpcoList}"
SelectionMode="Single" SelectedItem="{Binding Mode=TwoWay, Path=SelectedOpco}">
But whenever I set some value to SelectedOpco in my VM it does not show as selected in ListBox.
Any ideas? Thanks in advance!

Some things you could try:
Simplify the setter to something simpler (it should do what you expect to - check if a different element of the list is selected).
set {
if(_selectedOpco != value) {
_selectedOpco = value;
RaisePropertyChanged("SelectedOpco");
}
}
Put a break point at the RaisePropertyChanged("SelectedOpco"); line to see if the execution goes there. If yes, check if the class is really marked as INotifyPropertyChanged.
if(!(this is INotifyPropertyChanged))
throw new Exception("Not INotifyPropertyChanged");
Try to set the value with:
SelectedOpco = OpcoList.FirstOrDefault(); // LastOrDefault
Good luck!

Related

ListBox ItemsSource binding path error. What am I missing?

I've having trouble understanding a ListBox ItemsSource binding error I'm getting. I followed the details on the Gong WPF.DragDrop GitHub project but I'm getting this error on my binding path:
The project here is what I'm working on and shows the issue. I've extended a little by using a generic type on my observable item so i can make the ListBoxItemViewModel reusable but I found that going without the generic type it still fails.
This is the XAML used for the ListBox.
<ListBox Width="300px" x:Name="clothingItems" Margin="10px" AllowDrop="True"
ItemsSource="{Binding Items}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DragHandler="{Binding}"/>
This is the code for my ListBoxViewModel.
public class ListBoxViewModel<TItemVm> : IDropTarget
where TItemVm : class, IDropTargetItemViewModel<TItemVm>
{
public ObservableCollection<TItemVm> Items = new ObservableCollection<TItemVm>();
public void DragOver(IDropInfo dropInfo)
{
var sourceItem = dropInfo.Data as TItemVm;
var targetItem = dropInfo.TargetItem as TItemVm;
if (sourceItem != null && targetItem != null && targetItem.CanAcceptChildren)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = System.Windows.DragDropEffects.Copy;
}
}
public void Drop(IDropInfo dropInfo)
{
var sourceItem = dropInfo.Data as TItemVm;
var targetItem = dropInfo.TargetItem as TItemVm;
targetItem.Children.Add(sourceItem);
}
}
ListBoxViewModel has a public ObservableCollection property called Items and an instance is assigned as the DataContext so why wouldn't ItemsSource="{Binding Items}" resolve?
The issue is that Items in ListBoxViewModel<TItemVm> is a field, not a property.
public ObservableCollection<TItemVm> Items = new ObservableCollection<TItemVm>();
Change the member like below to make it a property and enable binding it.
public ObservableCollection<TItemVm> Items { get; } = new ObservableCollection<TItemVm>();
See Binding Source Types for more information on which binding sources can be used in WPF.

WPF listbox removing item properly

Hi I am making an WPF application and have a problem with a listbox/listview, MVVM is implemented. I am creating a list of a class that is displayed on the listbox and I am editing the items through selecting an item in the listbox. The problem is when I am deleting an item it doesn't trigger onpropertychanged event to the UI, but is however working in the code, the values are right. When I close the window and reopens it then the list is updated, but not directly when the item is deleted, it never triggers onpropertychanged event for some reason.
It does work to just filter the quicknotelist like
quicknotelist = quicknotelist.where(x => x.id != selecteditem.id);
It works only once though and the UI updates however the selecteditem doesn't seem to work properly even though I am declaring
selecteditem = new quicknote() {*values*};
Part of relevant code, I am using INotifyPropertyChanged
private QuickNote selectedNote = new QuickNote(); // weeeeeee
public QuickNote SelectedNote
{
get
{
return selectedNote;
}
set
{
if (SelectedNote != null)
{
selectedNote = value;
OnPropertyChanged("SelectedNote");
EnableEditNoteBox = true;
}
}
}
private List<QuickNote> quickNoteList = new List<QuickNote>();
public List<QuickNote> QuickNoteList
{
get { return quickNoteList; }
set { quickNoteList = value; OnPropertyChanged("QuickNoteList"); }
}
here is the method that deletes the item
private void DeleteNote(object obj)
{
if (SelectedNote != null)
{
QuickNoteList.Remove(SelectedNote);
// I want this to trigger onpropertychanged without using myclasslist = newclasslist; since it messes up selecteditem to null.
}
}
heres the xaml part.
<ListBox
Width="713"
Height="230"
SelectedItem="{Binding SelectedNote, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding QuickNoteList,BindsDirectlyToSource=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
DisplayMemberPath="Notes"
Foreground="Black"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.CanContentScroll="False"/>
I'd leave a comment if I could. You should lookup ObservableCollection. I think QuickNoteList should be of this type.

Modify the SelectedItem works not with MVVM

I have a window with a combobox. This comboboxhas 5 ComboboxItems.
In the example I want that it is not possible to select the items 3, 4 and 5.
I've tried two different ways: MVVM way and codebehind way
MVVM way:
xaml:
<ComboBox SelectedIndex="{Binding Path=SaveIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Path=SaveSelectedItemCheck}" Name="SaveCombobox">
viewmodel:
public object SaveSelectedItemCheck
{
get { return _control.SaveCombobox.Items[CurrentSaveIndex]; }
set
{
if (value != _control.SaveCombobox.Items[0] && value != _control.SaveCombobox.Items[1])
{
OnPropertyChanged("SaveSelectedItemCheck");
}
}
}
codebehind way:
xaml:
<ComboBox SelectedIndex="{Binding Path=SaveIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectionChanged="Save_SelectionChanged">
codebehind:
private void Save_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox combobox = sender as ComboBox;
if(combobox == null)
{
return;
}
if (combobox.SelectedItem != combobox.Items[0] && combobox.SelectedItem != combobox.Items[1])
{
combobox.SelectedItem = combobox.Items[1];
e.Handled = true;
}
}
But it only works with the codebehind way, which is dirty.
Why doesn't work the MVVM way?
As others said, you do not actually set any value in the property setter.
But more important IMO, I think you've misunderstood the MVVM key concepts. There are lots of issues with your ViewModel code:
public object SaveSelectedItemCheck
{
get { return _control.SaveCombobox.Items[CurrentSaveIndex]; }
set
{
if (value != _control.SaveCombobox.Items[0] && value != _control.SaveCombobox.Items[1])
{
OnPropertyChanged("SaveSelectedItemCheck");
}
}
}
You're referring to _control.SaveCombobox.Items, which are UI concept/objects. This isn't the goal of the ViewModel. And you're returning an object, you should strongly type your model!
What you should have is the following:
a model (strongly typed POCO classes)
ViewModels that do not deal with the view controls in any way (you could even separate views and ViewModels into different assemblies to ensure you're following this rule)
Views, with binding to ItemsSource for control such as Combobox
Model:
public class SomeObject : INotifyPropertyChanged
{
private string someProperty;
public string SomeProperty
{
get { return this.someProperty; }
set
{
if (this.someProperty != value)
{
this.someProperty = value;
OnPropertyChanged("SomeProperty");
}
}
}
...
}
ViewModel:
public class ViewModel : SomeViewModelBase
{
private ObservableCollection<SomeObject> items;
private SomeObject selectedItem;
public ObservableCollection<SomeObject> Items
{
get
{
return items;
}
set
{
if (this.items != value)
{
this.items = value;
OnPropertyChanged("Items");
}
}
}
public ObservableCollection<SomeObject> SelectedItem
{
get
{
return selectedItem;
}
set
{
if (this.selectedItem != value)
{
this.selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
...
// Anywhere in your view model:
this.Items = new ObservableCollection<SomeObject>(...);
this.SelectedItem = this.Items[2];
// Etc.
}
View:
<ComboBox
ItemsSource={Binding Items}
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
View code-behind:
Nothing for your example
Your ViewModel method doesn't set the value of the property - regardless of whether the value is valid or not. It just fires an event based on whether the value is valid.
In fact, on closer inspection you appear to have misunderstood the MVVM pattern somewhat, as it appears that your ViewModel code might be referring directly to the control it is supporting. You should have a backing field for your property as per a "normal" property.
More importantly, you should throw the PropertyChanged event whether the value is valid or not, because if the value has been overriden by the viewmodel then PropertyChanged will notify the UI that the combobox value needs to be re-set to a valid value.
You don't store any value in the setter in your MVVM way.

Unable to hide the control in WPF using MVVM

When i selected the values in combo box,i have to hide another control. I have write the code as shown in below. Please correct me where i made mistake in that.
View Code:
<ComboBox x:Name="cboShowRuleWhere" Height="20" Width="200" ItemsSource="{Binding Source={StaticResource listedView}, Path=FilterRules}" DisplayMemberPath="RuleName" SelectedValuePath="RuleId" SelectedValue="{Binding Source={StaticResource listedView}, Path=SelectedRuleName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ></ComboBox>
<ComboBox Height="21" HorizontalAlignment="Left" Margin="6,4,0,0" x:Name="cboRuleCondtion" Visibility="{Binding Path=IsButtonVisible,Converter={StaticResource BoolToVisible}}" VerticalAlignment="Top" Width="212" />
ViewModel Code:
private DataTable m_selectedRuleName;
public DataTable SelectedRuleName
{
get
{
return m_selectedRuleName;
}
set
{
m_selectedRuleName = value;
base.RaisePropertyChangedEvent("SelectedRuleName");
}
}
private bool _IsButtonVisible;
public bool IsButtonVisible
{
get { return _IsButtonVisible; }
set
{
_IsButtonVisible = value;
base.RaisePropertyChangedEvent("IsButtonVisible");
}
}
Where i have to correct? Please help me asap. Thanks in advance..
There's not a whole lot to go on here. For instance, where are you setting IsButtonvisible based on your rule criteria? Here are some ideas:
1) Don't create a backing field for IsButtonVisible. Instead, have it return the correct analysis.
public bool IsButtonVisible { get { return SelectedRuleName == "IsVisibleRule"; } }
2) You can fire the Notify Propery Changed event from anywhere. In this case, you want the IsButtonVisible binding to be reevaluated every time the SelectedRuleName changes:
private DataTable m_selectedRuleName;
public DataTable SelectedRuleName
{
get
{
return m_selectedRuleName;
}
set
{
m_selectedRuleName = value;
base.RaisePropertyChangedEvent("SelectedRuleName");
base.RaisePropertyChangedEvent("IsButtonVisible");
}
}
3) Is the SelectedRuleName really a DataTable? That would seem odd to me because it indicates multiple rows. It would be a longer post, but I would avoid DataTable altogether and change the ComboBox item source to an ObservableCollection. The "SelectedRuleName" would be of type T (not DataTable).
4) Along the same lines, I have found much greater success using SelectedItem instead of SelectedValue.
I hope some of this helps.

WPF Databinding With A Collection Object

I have a simple class as defined below:
public class Person
{
int _id;
string _name;
public Person()
{ }
public int ID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
that is stored in a database, and thru a bit more code I put it into an ObservableCollection object to attempt to databind in WPF later on:
public class People : ObservableCollection<Person>
{
public People() : base() { }
public void Add(List<Person> pListOfPeople)
{
foreach (Person p in pListOfPeople) this.Add(p);
}
}
In XAML, I have myself a ListView that I would like to populate a ListViewItem (consisting of a textblock) for each item in the "People" object as it gets updated from the database. I would also like that textblock to bind to the "Name" property of the Person object.
I thought at first that I could do this:
lstPeople.DataContext = objPeople;
where lstPeople is my ListView control in my XAML, but that of course does nothing. I've found TONS of examples online where people through XAML create an object and then bind to it through their XAML; but not one where we bind to an instantiated object and re-draw accordingly.
Could someone please give me a few pointers on:
A) How to bind a ListView control to my instantiated "People" collection object?
B) How might I apply a template to my ListView to format it for the objects in the collection?
Even links to a decent example (not one operating on an object declared in XAML please) would be appreciated.
You were so close.
lstPeople.ItemsSource = objPeople; // :)
The only other thing you need is how to apply a view for each item in your collection. No problem. I won't use a ListView... I'll just use an ItemsControl because it's a bit simpler.
<ItemsControl x:Name="lstPeople">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
That's pretty much it. The strategy is the same for a Listview, but you need to provide just a tad more XAML to provide column headers and stuff. I'm not 100% sure you need this at the moment, so I left it out.
Edit: Here's an extension method for "AddRange" that will do what you are trying to do by subclassing ObservableCollection. Little easier... especially if you end up with a lot of collection types (you will)
public static void AddRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items)
{
foreach(var item in items)
{
collection.Add(item);
}
}
Then you can just do:
ObservableCollection<Person> peeps = new ObservableCollection<Person>();
peeps.AddRange(new List<Person>
{
new Person() { Name = "Greg" },
new Person() { Name = "Joe" }
});
#Anderson beat me by seconds, my answer was very similar.
One thing i will add though: there is no need to define a new type of collection that inherits from ObservableCollection, instead you can just do this:
ObservableCollection<Person> myPeopleCollection = new ObservableCollection<Person>();
the only time you want to extend it is if you are going to be doing something different or fancy with it, which you don't appear to be doing.
Indeed 'Anderson Imes' response is correct (upvote), although I have two remarks:
First, if you only need to display one property of an object, I think it's easier and cleaner to use ListBox instead of ListView, because the code for binding the property will be reduced to
<ListBox x:Name="lstPeople" DisplayMemberPath="Name" />
Second, if you are using WPF and Binding, make sure your objects implement INotifyPropertyChanged, so that changes are always synchronized between UI and the objects.
public class Person : INotifyPropertyChanged
{
int _id;
string _name;
public Person()
{ }
public int ID
{
get { return _id; }
set {
_id = value;
RaisePropertyChanged("ID");
}
}
public string Name
{
get { return _name; }
set {
_name = value;
RaisePropertyChanged("Name");
}
}
pivate void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}

Resources