Im creating a listbox (devexpress control) like this
<dxe:ListBoxEdit x:Name="lstBoxFeatures" DisplayMember="Description" ValueMember="FeatureId" SelectionMode="Multiple"
ItemsSource="{Binding Path=DataContext.Features, RelativeSource={RelativeSource AncestorType=Window}}" Height="320"
EditValue="{Binding Path=DataContext.SelectedFeatures, RelativeSource={RelativeSource AncestorType=Window}}"
>
<dxe:ListBoxEdit.StyleSettings>
<dxe:CheckedListBoxEditStyleSettings />
</dxe:ListBoxEdit.StyleSettings>
</dxe:ListBoxEdit>
I have the view, i fill it like this
I filling the values like this (are using a list of features)
_CustomerLicense.Features = GetFeatureList(SelectedLicense.Product.ProductId);
_CustomerLicense.SelectedFeatures =_CustomerLicense.Features.Where(x => FeaturesIds.Contains(x.FeatureId)).ToList();
The process where i get ths list of features
private List<Feature> GetFeatureList(Project.Common.Domain.ProductEnum ProductID )
{
var Res = new List<Feature>();
var Features = new LicenseService().GetFeatures(ProductID);
Features.ForEach((x)=> {
Res.Add(new Feature(x));
});
return Res;
}
The view i have this way
public List<Feature> Features
{
get { return _Features;}
set
{
_Features = value;
this.NotifyPropertyChanged("Features");
}
}
public List<Feature> SelectedFeatures
{
get { return _SelectedFeatures; }
set
{
_SelectedFeatures = value;
NotifyPropertyChanged("SelectedFeatures");
}
}
When i run the app it sets the values, the listbox is populated with all the reatures in the list but the selected features are not checked.
Regards
Try declaring SelectedFeatures as a List of Objects (you'll need to change _SelectedFeatures too):
public List<object> SelectedFeatures
{
get { return _SelectedFeatures; }
set
{
_SelectedFeatures = value;
NotifyPropertyChanged("SelectedFeatures");
}
}
and change this line:
_CustomerLicense.SelectedFeatures =_CustomerLicense.Features.Where(x => FeaturesIds.Contains(x.FeatureId)).Cast<object>().ToList();
The SelectedFeatures collection should contain a subset of the Feature instances that are in the Features collection. Try this:
var features = GetFeatureList(SelectedLicense.Product.ProductId);
_CustomerLicense.Features = features;
var selectedFeatureIds = _CustomerLicense.Features.Where(x => FeaturesIds.Contains(x.FeatureId)).Select(x => x.FeatureId).ToList();
_CustomerLicense.SelectedFeatures = features.Where(x => selectedFeatureIds.Contains(x.FeatureId)).ToList();
Related
I am trying to populate a collection view from a ViewModel, however when I try to bind the data to the collection view, the ViewModel is null.
xaml.cs file
ObservableCollection<ReportsClass> newKidList = new ObservableCollection<ReportsClass>();
public ReportsViewModel viewmodel { get; set; }
public ReportsPage()
{
InitializeComponent();
viewmodel = new ReportsViewModel();
this.BindingContext = viewmodel;
PreviousDateRange.CornerRadius = 20;
NextDateRange.CornerRadius = 20;
DateTime firstDate = currentDate.StartOfWeek(DayOfWeek.Sunday);
DateTime secondDate = currentDate.AddDays(7).StartOfWeek(DayOfWeek.Saturday);
DateRange.Text = firstDate.ToString("MMMM d") + " - " + secondDate.ToString("MMMM d");
Kids.SetBinding(ItemsView.ItemsSourceProperty, nameof(viewmodel.kids));
}
Here is my view model
public class ReportsViewModel
{
public ObservableCollection<ReportsClass> kids { get; set; }
FirebaseStorageHelper firebaseStorageHelper = new FirebaseStorageHelper();
WebServiceClass webServiceClass = new WebServiceClass();
DateTime currentDate = DateTime.Now;
public ReportsViewModel()
{
GetKids();
}
public async void GetKids()
{
var parentId = await SecureStorage.GetAsync("parentid");
kids = await webServiceClass.Reports(Convert.ToInt32(parentId), currentDate.StartOfWeek(DayOfWeek.Sunday), currentDate.AddDays(7).StartOfWeek(DayOfWeek.Saturday));
}
}
And here is the method that gets the data for the view model
public async Task<ObservableCollection<ReportsClass>> Reports(int parentid, DateTime startDate, DateTime endDate)
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("parentid", parentid.ToString()),
new KeyValuePair<string, string>("startDate", startDate.ToString("yyyy-MM-dd H:mm:ss")),
new KeyValuePair<string, string>("endDate", endDate.ToString("yyyy-MM-dd"))
});
var response = await client.PostAsync(string.Format("https://example.com/api/index.php?action=reports"), content);
var responseString = await response.Content.ReadAsStringAsync();
ObservableCollection<ReportsClass> items = JsonConvert.DeserializeObject<ObservableCollection<ReportsClass>>(responseString);
return items;
}
What am I doing wrong? The purpose of me doing it this way is so I can update an item in the collectionview
Here is my ReportsClass
public class ReportsClass
{
public ReportsClass(string firstName)
{
first_name = firstName;
}
public string first_name { get; set; }
}
OPTION A:
Fix the syntax of Kids.SetBinding, to not get null. Refer to the CLASS ReportsViewModel, not to the INSTANCE viewmodel:
Kids.SetBinding(ItemsView.ItemsSourceProperty, nameof(ReportsViewModel.kids));
The kids still won't appear in list. To fix, kids needs OnPropertyChanged:
public ObservableCollection<ItemModel> kids {
get => _kids;
set {
_kids = value;
OnPropertyChanged();
}
}
private ObservableCollection<ItemModel> _kids;
See the other code in Option B. Adapt as desired.
When you need XAML to see a DYNAMIC change, you need OnPropertyChanged. This is an implementation of INotifyPropertyChanged. Add this call to properties (that XAML binds to) of ReportsClass:
// Inheriting from `BindableObject` is one way to obtain OnPropertyChanged method.
public class ReportsClass : Xamarin.Forms.BindableObject
{
public ReportsClass(string firstName)
{
first_name = firstName;
}
public string first_name {
get => _first_name;
set {
_first_name = value;
// This tells XAML there was a change.
// Makes "{Binding first_name}" work dynamically.
OnPropertyChanged();
}
}
private string _first_name;
}
OPTION B:
Didn't find an answer anywhere that does everything correctly, so here is a complete sample, for future reference:
Remove Kids.SetBinding(...). (It can be fixed as shown in OPTION A, but its easier to get it correct in XAML, so below I show it in XAML.)
Bindings from Page to VM. See xaml below.
Create ObservableCollection with setter that does OnPropertyChanged. This informs XAML when the list is ready, so page updates. (This is an implementation of INotifyPropertyChanged, as Jason mentioned.)
Use Device.BeginInvokeOnMainThread(async () to create an async context, that is queued to run after constructor returns. (This fixes the issue Jason mentioned, which is that a constructor isn't an async context, so should not DIRECTLY call an async method such as QueryItemsAsync, or your GetKids.) This is more reliable.
PageWithQueryData.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestXFUWP.PageWithQueryData">
<ContentPage.Content>
<StackLayout>
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.EmptyView>
<Grid>
<Label Text="Loading ..." FontSize="24" TextColor="Blue" BackgroundColor="LightBlue" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
</Grid>
</CollectionView.EmptyView>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
PageWithQueryData.xaml.cs:
public partial class PageWithQueryData : ContentPage
{
public PageWithQueryData()
{
InitializeComponent();
// ... other initialization work here ...
// BUT remove `Kids.Binding(...);` line. See XAML: `ItemsSource="{Binding Items}"`.
BindingContext = new VMWithQueryData();
}
}
VMWithQueryData.cs:
class VMWithQueryData : Xamarin.Forms.BindableObject
{
public VMWithQueryData()
{
// Start an async task to query.
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () => {
await QueryItemsAsync();
});
// Alternative implementation: Start a background task to query.
//QueryItemsInBackground();
}
public ObservableCollection<ItemModel> Items {
get => _items;
set {
_items = value;
OnPropertyChanged();
}
}
private ObservableCollection<ItemModel> _items;
private async Task QueryItemsAsync()
{
var names = new List<string> { "One", "Two", "Three" };
bool queryOneAtATime = false;// true;
if (queryOneAtATime) {
// Show each item as it is available.
Items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
// Simulate slow query - replace with query that returns one item.
await Task.Delay(1000);
Items.Add(new ItemModel(name));
}
} else {
// Load all the items, then show them.
// Simulate slow query - replace with query that returns all data.
await Task.Delay(3000);
var items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
items.Add(new ItemModel(name));
}
Items = items;
}
}
// Alternative implementation, using a background thread.
private void QueryItemsInBackground()
{
Task.Run(() => {
var names = new List<string> { "One", "Two", "Three" };
bool queryOneAtATime = false;// true;
if (queryOneAtATime) {
// Show each item as it is available.
Items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
// Simulate slow query - replace with query that returns one item.
System.Threading.Thread.Sleep(1000);
Items.Add(new ItemModel(name));
}
} else {
// Load all the items, then show them.
// Simulate slow query - replace with query that returns all data.
System.Threading.Thread.Sleep(3000);
var items = new ObservableCollection<ItemModel>();
foreach (var name in names) {
items.Add(new ItemModel(name));
}
Items = items;
}
});
}
}
ItemModel.cs:
public class ItemModel
{
public ItemModel(string name)
{
Name = name;
}
public string Name { get; set; }
}
This also demonstrates <CollectionView.EmptyView> to display a message to user, while the data is being queried.
For completeness, I've included an alternative QueryItemsInBackground, that uses a background thread instead of an async method. Either approach works well.
Notice inheritance from Xamarin.Forms.BindableObject. This is one way to get an implementation of INotifyPropertyChanged. You can use any other MVVM library or technique.
Move this line of code to the end of your constructor
this.BindingContext = viewmodel;
I have searched Google for a simple solution to this but no luck. I have a standard WPF combo box which I would simply like to be able to filter the list displayed according to the first 2 or 3 letters a users types when the combo box has focus. I tried some coding including some lamba expressions but the error "System.NotSupportedException" keeps getting thrown on the line where "combobox.Items.Filter" is specified. I'm not using MVVM and would just like this simple functionality available for the user. Please help! P.S. IsEditable, IsTextSearchEnabled and StaysOpenOnEdit properties are set to true but the desired functionality is not yet achieved.
I have developed a sample application. I have used string as record item, you can do it using your own entity. Backspace also works properly.
public class FilterViewModel
{
public IEnumerable<string> DataSource { get; set; }
public FilterViewModel()
{
DataSource = new[] { "india", "usa", "uk", "indonesia" };
}
}
public partial class WinFilter : Window
{
public WinFilter()
{
InitializeComponent();
FilterViewModel vm = new FilterViewModel();
this.DataContext = vm;
}
private void Cmb_KeyUp(object sender, KeyEventArgs e)
{
CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(Cmb.ItemsSource);
itemsViewOriginal.Filter = ((o) =>
{
if (String.IsNullOrEmpty(Cmb.Text)) return true;
else
{
if (((string)o).Contains(Cmb.Text)) return true;
else return false;
}
});
itemsViewOriginal.Refresh();
// if datasource is a DataView, then apply RowFilter as below and replace above logic with below one
/*
DataView view = (DataView) Cmb.ItemsSource;
view.RowFilter = ("Name like '*" + Cmb.Text + "*'");
*/
}
}
XAML
<ComboBox x:Name="Cmb"
IsTextSearchEnabled="False"
IsEditable="True"
ItemsSource="{Binding DataSource}"
Width="120"
IsDropDownOpen="True"
StaysOpenOnEdit="True"
KeyUp="Cmb_KeyUp" />
I think the CollectionView is what you are looking for.
public ObservableCollection<NdfClassViewModel> Classes
{
get { return _classes; }
}
public ICollectionView ClassesCollectionView
{
get
{
if (_classesCollectionView == null)
{
BuildClassesCollectionView();
}
return _classesCollectionView;
}
}
private void BuildClassesCollectionView()
{
_classesCollectionView = CollectionViewSource.GetDefaultView(Classes);
_classesCollectionView.Filter = FilterClasses;
OnPropertyChanged(() => ClassesCollectionView);
}
public bool FilterClasses(object o)
{
var clas = o as NdfClassViewModel;
// return true if object should be in list with applied filter, return flase if not
}
You wanna use the "ClassesCollectionView" as your ItemsSource for your Combobox
I got a combo box in wpf form
I set the ItemSource to the collection of Dictionary of (Pet Type) and just display the Value and hid the Key
public void BindComboBoxes()
{
this.cboTypes.ItemsSource = new BindingSource(CommonMgr.GetPetTypesDropDown(false), null);
this.cboTypes.DisplayMemberPath = "Value";
this.cboTypes.SelectedValuePath = "Key";
}
Then whenever I type to encode a new Breed Object, and type a text in the cboTypes of something that doesn't exist in its items(not in the db), my program will ask if the end user wants to add that new PetType in the db, if yes, then it will do so.
Then i update the cboTypes using the BindComboBoxes method again, set the cboTypes.Text into the new item and assign the Key to the designated field, but the problem is, it says, it was null. it worked fine in the windows form though. Here's my code:
public Breed GetPageEntity()
{
Breed setEntity = new Breed();
bool doesExist = false;
setEntity.Id = DefaultValue.GetInt(this.txtId.Text);
setEntity.BreedName = DefaultValue.GetString(this.txtName.Text);
try
{
setEntity.PetTypeId = DefaultValue.GetInt(this.cboTypes.SelectedValue.ToString());
}
catch (Exception)
{
var addAnother = MessageBox.Show(String.Format("{0}: This type is not in the database. \nAdd {0} to the database?",
this.cboTypes.Text), "Pet Type Cannot Be Found", MessageBoxButtons.OKCancel);
if (addAnother == System.Windows.Forms.DialogResult.OK)
{
petTypeMgr.Entity = this.PetTypeAdder(cboTypes.Text);
string temp = this.cboTypes.Text;
petTypeMgr.Insert((petTypeMgr.Entity), fUser.Entity.Id, ref doesExist);
//cboTypes.ItemsSource = null;
//cboTypes.Items.Clear();
BindComboBoxes();
cboTypes.Text = temp;
//SelectedValue became null
setEntity.PetTypeId = DefaultValue.GetInt(this.cboTypes.SelectedValue);
}
}
setEntity.Description = DefaultValue.GetString(this.txtDescription.Text);
setEntity.SortOrder = DefaultValue.GetInt(txtSortOrder.Text);
setEntity.StatusId = true;
return setEntity;
}
You'll find it much easier if you data bind to properties in the code behind:
// Implement INotifyPropertyChanged interface properly here
private Dictionary<string, Pet> yourProperty = new Dictionary<string, Pet>();
public Dictionary<string, Pet> YourProperty
{
get { return yourProperty; }
set
{
yourProperty = value;
NotifyPropertyChanged("YourProperty");
}
}
private KeyValuePair<string, int> yourSelectedProperty;
public KeyValuePair<string, int> YourSelectedProperty
{
get { return yourSelectedProperty; }
set
{
yourSelectedProperty = value;
NotifyPropertyChanged("YourSelectedProperty");
}
}
Then in the XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding YourProperty}" DisplayMemberPath="Value"
SelectedValuePath="Key" SelectedItem="{Binding YourSelectedProperty}" />
<TextBlock Grid.Column="1" Text="{Binding YourSelectedProperty.Key}" />
</Grid>
You only need to set your ItemsSource once like this. Once it is data bound to a collection property, you can just make changes to the collection and they will update in the UI automatically. So, assuming that your GetPetTypesDropDown method returns the correct type, you should be able to update the ComboBox items like this:
YourProperty = CommonMgr.GetPetTypesDropDown(false);
Alternatively, you could equally do something like this to update it:
YourProperty = new Dictionary<string, int>();
foreach (YourDataType dataType in CommonMgr.GetPetTypesDropDown(false))
{
YourProperty.Add(dataType.Key, dataType.Value);
}
Why don't you just bind Breed to the Combobox?
In the Breed class override the ToString() method so that the box shows what you want it to.
class Breeds
{
//Variables
public void override ToString()
{
return Breedname;
}
}
Set the combobox
List<Breeds> breedlist = new List<Breeds>();
this.cboTypes.ItemsSource = breedlist;
Read the combobox
if(cboTypes.SelectedItem != null)
{
Breeds breed = (Breeds)cboTypes.SelectedItem;
//Do stuff
}
else
{
//Create new breed
}
i have a DXTabControl. The DXTabItems are generated via my ViewModel.
//MainViewModel
public MainViewModel()
{
var items = new ObservableCollection<DXTabItem>();
items.Add(
new DXTabItem()
{
Header = "Test1",
Content = new WebViewModel()
});
items.Add(
new DXTabItem()
{
Header = "Test2",
Content = new CMSViewModel()
});
TabItems = items;
}
private ObservableCollection<DXTabItem> _tabItems;
public ObservableCollection<DXTabItem> TabItems
{
get { return _tabItems; }
set { SetProperty(ref _tabItems, value, () => TabItems); }
}
I am working with a DataTemplate and my TabItem is still not showing any UserControl.
//MainView.xaml
<DataTemplate x:Key="WebTemplate" DataType="{x:Type viewmodel:WebViewModel}">
<view:WebView/>
</DataTemplate>
<DataTemplate x:Key="CMSTemplate" DataType="{x:Type viewmodel:CMSViewModel}">
<view:CMSView/>
</DataTemplate>
<datatemplate:TemplateSelector x:Key="DataTemplateSelector"
WebTemplate="{StaticResource WebTemplate}"
CMSTemplate="{StaticResource CMSTemplate}" />
<dx:DXTabControl ItemsSource="{Binding TabItems}" ItemTemplateSelector="{StaticResource DataTemplateSelector}" />
//DataTemplateSelector
public class TemplateSelector : DataTemplateSelector
{
public DataTemplate WebTemplate { get; set; }
public DataTemplate CMSTemplate { get; set; }
public override DataTemplate SelectTemplate(Object item,
DependencyObject container)
{
if (item == null) return base.SelectTemplate(item, container);
if (item.GetType() == typeof(WebViewModel))
{
return WebTemplate;
}
else if (item.GetType() == typeof(CMSViewModel))
{
return CMSTemplate;
}
else return base.SelectTemplate(item, container);
}
}
Everything is working, except showing the content i need. No view is been shown. Any idea? Did i miss something?
The following answer is based on caliburn.micro.
Step 1: Add a convention to the bootstrapper
public Bootstrapper()
{
ConventionManager.AddElementConvention<DXTabControl>(DXTabControl.ItemsSourceProperty, "ItemsSource", "DataContextChanged")
.ApplyBinding = (viewModelType, path, property, element, convention) =>
{
if (!ConventionManager.SetBindingWithoutBindingOrValueOverwrite(viewModelType, path, property, element, convention, DXTabControl.ItemsSourceProperty))
{
return false;
}
var tabControl = (DXTabControl)element;
if (tabControl.ItemTemplate == null && tabControl.ItemTemplateSelector == null && property.PropertyType.IsGenericType)
{
var itemType = property.PropertyType.GetGenericArguments().First();
if (!itemType.IsValueType && !typeof(string).IsAssignableFrom(itemType))
{
tabControl.ItemTemplate = ConventionManager.DefaultItemTemplate;
}
}
ConventionManager.ConfigureSelectedItem(element, Selector.SelectedItemProperty, viewModelType, path);
if (string.IsNullOrEmpty(tabControl.DisplayMemberPath))
{
ConventionManager.ApplyHeaderTemplate(tabControl, DXTabControl.ItemHeaderTemplateProperty, DXTabControl.ItemHeaderTemplateSelectorProperty, viewModelType);
}
return true;
};
[...]
}
Now you can bind any Screen-Collection to your DXTabControl.
Step 2: Create a collection in the ViewModel
public class MainViewModel : Screen
{
public MainViewModel()
{
DisplayName = "DevExpress Test Environment";
}
private static BindableCollection<Screen> _tbCtrl = new BindableCollection<Screen>();
public BindableCollection<Screen> TbCtrl
{
get { return _tbCtrl; }
set
{
_tbCtrl = value;
NotifyOfPropertyChange(() => TbCtrl);
}
}
}
You can e.g. put any other ViewModel which is based on the Screen class to your collection. That means, you will be able to display your content for each tabitem.
Step 3: Create the DXTabControl in your View (XAML-Code)
<dx:DXTabControl x:Name="TbCtrl" />
Give it a go. Open for feedback.
/// Alternative solution without Caliburn.Micro
Step 1: Add the DXTabControl to your MainView (XAML-Code)
<dx:DXTabControl ItemsSource="{Binding TbCtrlItems}" />
Step 2: Your MainViewModel needs to add those items like i have described above (in my question), but in this case, you have to specify the content-property
public MainViewModel()
{
_tbCtrlItems.Add(new DXTabItem()
{
Header = "Test1",
Content = new Views.View1() {DataContext = new ViewModel1()}
});
_tbCtrlItems.Add(new DXTabItem()
{
Header = "Test2",
Content = new Views.View2() { DataContext = new ViewModel2() }
});
}
private ObservableCollection<DXTabItem> _tbCtrlItems = new ObservableCollection<DXTabItem>();
public ObservableCollection<DXTabItem> TbCtrlItems
{
get { return _tbCtrlItems; }
set { SetProperty(ref _tbCtrlItems, value, () => TbCtrlItems); }
}
I hope this answer is helpful.
My Senario: I have two ListBoxes bound to two different ObervableCollections--a collection of shapes and a collection of colors user may select from to find part numbers with matching attribute criteria. (More attribute collections exist in my app but I am omitting them for clarity.)
Upon a selection from either of the two attribute value-containing listboxes, I gather the resulting part numbers possessing the selected attribute property in a collection called ResultingPNsIntersect. (The collection of resulting part numbers is displayed in the third ListBox.)
What should happen: after making a selection in either of the two listboxes, the intersection of resulting part numbers possesing that Selected'Attribute' should update so that only relevant part numbers remain. If a shape has been selected, the listbox holding the ColorsCollection must update so that only color attributes (in ColorsCollection) relevant to PartNumbers that have the SelectedShape are displayed in the second listbox.
My Problem: After selecting a shape, the ResultingPNsIntersect ObservableCollection updates, but the PropertyChanged notification for ColorsCollection is never fired, so the second listbox never updates to give the user the updated color attributes to choose from.
I've done this before in other apps without any problems. I see no need to subscribe to CollectionChanged since I am not editing property values in ResultingPNsIntersect--I am replacing the collection with new values. Please help me see where my code is failing and why so that I can better understand the conditions INPCs require for firing.
The xaml binding:
<ListBox x:Name="SelectFromAvailableShapesLB" DockPanel.Dock="Top"
ItemsSource="{Binding AvailableShapesCollection}"
DisplayMemberPath="AttVal"
SelectedItem="{Binding SelectedShape, Mode=TwoWay}"/>
<ListBox x:Name="SelectFromAvailableColorsLB" DockPanel.Dock="Top"
ItemsSource="{Binding AvailableColorsCollection}"
DisplayMemberPath="AttVal"
SelectedItem="{Binding SelectedColor, Mode=TwoWay}"/>
<ListBox x:Name="PnsResultingFromAttributeSelectionsLB" DockPanel.Dock="Top"
ItemsSource="{Binding ResultingPNsIntersect}"
DisplayMemberPath="PartNum"
SelectedItem="{Binding SelectedPartNum, Mode=TwoWay}"/>
My ViewModel:
public ObservableCollection<AttributeValuesLibrary> AvailableShapesCollection
{
get
{
if (_resultingPNsIntersect != null)
{
foreach (PartNumber shape in _resultingPNsIntersect.Where(x => x.ShapeID != null))
{
if (!_availableShapesCollection.Contains(shape.AttributeValuesLibrary_Shape))
{
this._availableShapesCollection.Add(shape.AttributeValuesLibrary_Shape);
}
}
}
return _availableShapesCollection;
}
set
{
if (_availableShapesCollection != value)
{
this._availableShapesCollection = value;
RaisePropertyChanged("AvailableShapesCollection");
}
}
}
public ObservableCollection<AttributeValuesLibrary> AvailableColorsCollection
{
get
{
if (_resultingPNsIntersect != null)
{
foreach (PartNumber color in _resultingPNsIntersect.Where(x => x.ColorID != null))
{
if (!_availableColorsCollection.Contains(color.AttributeValuesLibrary_Color))
{
_availableColorsCollection.Add(color.AttributeValuesLibrary_Color);
}
}
}
return _availableColorsCollection;
}
set
{
if (_availableColorsCollection != value)
{
_availableColorsCollection = value;
RaisePropertyChanged("AvailableColorsCollection");
}
}
}
public AttributeValuesLibrary SelectedShape
{
get
{
return _selectedShape;
}
set
{
if (_selectedShape != value)
{
_selectedShape = value;
RaisePropertyChanged("SelectedShape");
RaisePropertyChanged("ResultingPNsIntersect");
}
}
}
public ObservableCollection<ConnectorPartNumber> ConnAttPNResults
{
get
{
// If a shape has been selected, we need to navigate to it's related PartNumbers and add those to the intersection
// contained by ResultingPNsIntersection.
if (_selectedShape != null)
{
var shapeResults = _context.PartNumbers.Where(x => x.AttributeValuesLibrary_Shape.AttValID == _selectedShape.AttValID);
if (_resultingPNsIntersect != null)
{
var resultsFromPreviousSelection = _resultingPNsIntersect;
_resultingPNsIntersect = new ObservableCollection<PartNumber>(resultsFromPreviousSelection.Intersect(shapeResults));
}
else if (_resultingPNsIntersect == null)
{
_resultingPNsIntersect = new ObservableCollection<PartNumber>(shapeResults);
}
}
return _resultingPNsIntersect;
}
set
{
if (_resultingPNsIntersect != value)
{
this._resultingPNsIntersect = value;
RaisePropertyChanged("ResultingPNsIntersect");
RaisePropertyChanged("AvailableColorsCollection"); <--Not firing!!!!!
}
}
}
Thanks in advance!
::UPDATE:: I can force this to work if I put the RaisePropertyChanged("AvailableColorsCollection") in the setter of my SelectedShape. But it makes less sense there, of course, because AvailableColorsCollection is dependent upon the ResultingPNsIntersect Collection which changes based on a selection in the attribute listboxes.
I think in general your approach is going to give you issues. Instead of implementing the logic in the getters of your properties, you should consider making your properties much "dumber". I would move this logic out of the properties into a helper method that modifies your available options:
private readonly ObservableCollection<AttributeValuesLibrary> _availableShapesCollection =
new ObservableCollection<AttributeValuesLibrary>();
private readonly ObservableCollection<AttributeValuesLibrary> _availableColorsCollection =
new ObservableCollection<AttributeValuesLibrary>();
public ObservableCollection<AttributeValuesLibrary> AvailableShapesCollection
{
get { return _availableShapesCollection; }
}
public ObservableCollection<AttributeValuesLibrary> AvailableColorsCollection
{
get { return _availableColorsCollection; }
}
public AttributeValuesLibrary SelectedShape
{
get { return _selectedShape; }
set
{
if (_selectedShape != value)
{
_selectedShape = value;
RaisePropertyChanged("SelectedShape");
SelectedShapeChanged();
}
}
}
public ObservableCollection<ConnectorPartNumber> ConnAttPNResults
{
get { return _resultingPNsIntersect; }
set
{
if (_resultingPNsIntersect != value)
{
this._resultingPNsIntersect = value;
RaisePropertyChanged("ResultingPNsIntersect");
UpdateAvailableOptions();
}
}
}
private void SelectedShapeChanged()
{
// If a shape has been selected, we need to navigate to it's related
// PartNumbers and add those to the intersection contained by ResultingPNsIntersection.
if (_selectedShape != null)
{
var shapeResults = _context.PartNumbers.Where(x => x.AttributeValuesLibrary_Shape.AttValID == _selectedShape.AttValID);
if (_resultingPNsIntersect != null)
{
var resultsFromPreviousSelection = _resultingPNsIntersect;
ConnAttPNResults = new ObservableCollection<PartNumber>(resultsFromPreviousSelection.Intersect(shapeResults));
}
else
{
ConnAttPNResults = new ObservableCollection<PartNumber>(shapeResults);
}
}
}
private void UpdateAvailableOptions()
{
if (_resultingPNsIntersect != null)
{
_availableColorsCollection.Clear();
_availableShapesCollection.Clear();
foreach (PartNumber color in _resultingPNsIntersect.Where(x => x.ColorID != null).Distinct())
{
_availableShapesCollection.Add(color.AttributeValuesLibrary_Color);
}
foreach (PartNumber shape in _resultingPNsIntersect.Where(x => x.ShapeID != null).Distinct())
{
shapes.Add(shape.AttributeValuesLibrary_Shape);
}
}
}
If you would rather have settable properties, the UpdateAvailableOptions could create new collections and set the AvailableColorsCollection & AvailableShapesCollection properties to the new instances (but then there is no need for ObservableCollections).
I would even take this a bit further actually. Since you don't want anybody to alter your Available collections, I would make them return ReadonlyObersvableCollection instances, and make their return types IEnumerable<T>.