I'm trying to display the content of a table in a combobox.
I'm using the MVVM pattern and in my viewmodel class if I write this it works:
private IEnumerable<EventType> _eventTypes;
public ManageProfileModel()
{
_referenceData = new ReferenceDataContext();
_referenceData.Load(_referenceData.GetEventTypesQuery(), false);
_eventTypes = _referenceData.EventTypes;
}
Like this the combobox is displaying the data.
However, I want the _eventTypes to be a List:
private List<EventType> _eventTypes;
But if I write this:
public ManageProfileModel()
{
_referenceData = new ReferenceDataContext();
_referenceData.Load(_referenceData.GetEventTypesQuery(), false);
_eventTypes = _referenceData.EventTypes.ToList();
}
then the combobox is empty. What is wrong with that?
I want to use a List, because I want to be able to add and remove data in the list.
Best regards.
If I remember correctly, you can not convert IEnumerable to IList directly. It is little tricky. I would use of the options from the following link. I have it in bookmark since I ran into the same problem.
http://devlicio.us/blogs/derik_whittaker/archive/2008/03/28/simple-way-to-convert-ienumerable-lt-entity-gt-to-list-lt-ientity-gt.aspx
or look at this link
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/af225aa0-1cf4-40dd-ac3e-e7a19edaef00
DomainContext.Load is asynchronous, so in your second example you're creating a list that's most likely empty because the EntitySet hasn't finished loading yet. Use the code posted by StackOverflowException to defer creating the list until the EntitySet has been populated and it should work.
just a shot straight from the head...
did you try to add something like propertychanged event for your list?
so it could be that the data came async and the property was not informed about the change...
like I said ...
private List<EventType> _eventTypes;
public List<EventType> EventTypes
{
get { return _eventTypes; }
set
{
_eventTypes = value;
RaisePropertyChanged("EventTypes");
}
}
and take also a look at ObservableCollections...
Like I said just a shot...
Hope this helps
I don't have much MVVM exposure but with silverlight + RIA, I usually do something like this.
private List<EventType> _eventTypes;
public ManageProfileModel()
{
_referenceData = new ReferenceDataContext();
var op = _referenceData.Load(_referenceData.GetEventTypesQuery(), false);
op.Completed += op_Completed;
}
void po_Completed(object sender, EventArgs e)
{
var op = ( InvokeOperation<IEnumerable<EventType>>)sender;
_eventTypes = op.Value.ToList();
}
Related
I am testing the workings of WPF with Entity Framework. I have a SS table called Vendors {VendorCode, VendorName, Phone}.
I am sticking with only EF and I am able to display and navigate the recordset on a WPF form with buttons first, next, last etc. I used the instructions on the MSDN site (Create a simple data application with WPF and Entity Framework 6)
My problem is the recordset is sorted only in the order it was entered into SS. I would like to sort it by VendorCode or by VendorName to make it easier on the user. I can't seem to make it sort the recordset or table data coming through EF.
Can you please help? Thank you!
Here is a snippet of my code:
public Vendor newVendor { get; set; }
VendorsEntities context = new VendorsEntities();
CollectionViewSource VendorViewSource;
public MainWindow()
{
InitializeComponent();
newVendor = new Vendor();
VendorViewSource = ((CollectionViewSource)
(FindResource("VendorViewSource")));
DataContext = this;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// this next line doesn't do it
context.Vendors.OrderBy(Vendor => Vendor.VendorCode);
context.Vendors.Load();
VendorViewSource.Source = context.Vendors.Local;
}
private void NextCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
VendorViewSource.View.MoveCurrentToNext();
}
You would need to set the result of OrderBy method in to some variable and then use that as OrderBy will return a new reference, or you can use the set the reference of context.Vendors to the reference returned by OrderBy() method.
Try doing it like:
var ordered = context.Vendors.OrderBy(Vendor => Vendor.VendorCode);
VendorViewSource.Source = ordered;
another way can be to order it after bringing the result back, but it is not a recommended approach, first approach should be preferred, but just giving another option which is also possible:
var vendors = context.Vendors.Load().OrderBy(Vendor => Vendor.VendorCode);
VendorViewSource.Source = vendors;
Hope it helps!
You are sorting context, not displayed items. Try:
VendorViewSource.Source = context.Vendors.OrderBy(Vendor => Vendor.VendorCode);
This is an attempt to expand on this question. In my WPF program I've been cloning tabItems by using an XamlWriter in a function called TrycloneElement. I originally found this function here, but the function can also be viewed in the link to my previous question.
Now that I am beginning to worry about functionality inside my program, I found that the TrycloneElement function does not replicate any code-behind functionality assigned to the tabItem that it is cloning.
Because of High Core's link and comment on my earlier question I decided to start implementing functionality on my tabItems through Data Binding with my ViewModel.
Here is a sample of a command that I've implemented:
public viewModel()
{
allowReversing = new Command(allowReversing_Operations);
}
public Command AllowReversing
{
get { return allowReversing; }
}
private Command allowReversing;
private void allowReversing_Operations()
{
//Query for Window1
var mainWindow = Application.Current.Windows
.Cast<Window1>()
.FirstOrDefault(window => window is Window1) as Window1;
if (mainWindow.checkBox1.IsChecked == true) //Checked
{
mainWindow.checkBox9.IsEnabled = true;
mainWindow.groupBox7.IsEnabled = true;
}
else //UnChecked
{
mainWindow.checkBox9.IsEnabled = false;
mainWindow.checkBox9.IsChecked = false;
mainWindow.groupBox7.IsEnabled = false;
}
}
*NOTE: I know that I cheated and interacted directly with my View in the above code, but I wasn't sure how else to run those commands. If it is a problem, or there is another way, please show me how I can run those same commands without interacting with the View like I did.
Now to the question:
After changing my code and adding the commands to my ViewModel, the TrycloneElement function no longer works. At run time during the tab clone I receive an XamlParseException on line, object x = XamlReader.Load(xmlReader); that reads:
I'm fine with ditching the function if there is a better way and I don't need it anymore. But ultimately, how do I take a tabItem's design and functionality and clone it? (Please keep in mind that I really am trying to correct my structure)
Thank you for your help.
Revision of Leo's answer
This is the current version of Leo's answer that I have compiling. (There were some syntax errors)
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues) })
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd
where dpd != null
select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly)
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
Here is my example of a properly-implemented dynamic TabControl in WPF.
The main idea is that each Tab Item is a separate widget that contains its own logic and data, which is handled by the ViewModel, while the UI does what the UI must do: show data, not contain data.
The bottom line is that all data and functionality is managed at the ViewModel / Model levels, and since the TabControl is bound to an ObservableCollection, you simply add another element to that Collection whenever you need to add a new Tab.
This removes the need for "cloning" the UI or do any other weird manipulations with it.
1.) To fix that XamlParseException, make sure you have a public constructor like an empty one, you probably defined a constructor and when you tried to serialize that object and deserialize it can't. You have to explicitly add the default constructor.
2.) I don't like the word clone, but I'd say, when they want to copy. I'll manually create a new tab item control then do reflection on it.
I have this code that I made
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] {new PropertyFilterAttribute(PropertyFilterOptions.SetValues)})
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd where dpd != null select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly))
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
So it would be like
var newTabItem = new TabItem();
newTabItem.CopyPropertiesFrom(masterTab);
I am writing my first application for WP8 platform in C#. I implemented three datatypes namely locationModel which has locationGroups. Each locationGroup has a ObservableCollection of type locationData.
locationData has two double types for latitude and longitude and a title string.
I used a textblock inside a stackpanel to show the locationData element's title, where the lat long are hidden to user.
There is a context menu on each of this textblock element which enable the user to delete the respective locationData.
When I open the app and delete any item, it succesfully does and updates the view too. But when I do it for another item it just doesnt work. I cannot delete more than one items for each time I open the app.
I used breakpoints to see where the problem is. the selected locationData is succesfully passed to the App.ViewModel.LocationModel.Items.Remove(). But it just that they are not deleted from the observable collection. I even tried to see the index of the locationData in observable collection and use RemoveAt method. Even it doesnt work.
I did a lot of research to find the problem, but no one seems to face the same problem as me. I referred to msdn article on how to implement inotifypropertychanged to update the collection. But its too complex for me to understand that.
I dont really understand why the observable collection delete the item for the second time even though if I pass the index of that item. And my usage of breakpoints showed me that the data is not even null.
So kindly tell me what is causing this problem and how do I overcome it so that I can implement my own workaround and not face this issue again. I can show you the code if you want me to.
Thanks.
CODE:
Adding an item
private void SaveLocationData(LocationData locationData)
{
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
try
{
App.ViewModel.Custom.Items.Add(locationData);
var data = JsonConvert.SerializeObject(App.ViewModel.Custom);
appSettings[LocationModel.CustomKey] = data;
appSettings.Save();
//Notify that data is changed
App.ViewModel.LoadData();
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
catch(IsolatedStorageException ex)
{
MessageBox.Show(ex.Message);
}
}
Deleting item:
private void DeleteLocationData(LocationData locationData)
{
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
try
{
App.ViewModel.Custom.Items.Remove(locationData);
var data = JsonConvert.SerializeObject(App.ViewModel.Custom);
appSettings[LocationModel.CustomKey] = data;
appSettings.Save();
//Notify that data is changed
App.ViewModel.LoadData();
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
catch (IsolatedStorageException ex)
{
MessageBox.Show(ex.Message);
}
}
One more thing that I want to say is, whenever I add a locationData to the collection, it updates automatically. Because adding is done on another page and when the mainpage.xaml loads(in which the observable collection data is), it updates automatically because of the code in OnNavigatedTo method.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
And LoadData method is :
public void LoadData()
{
Custom = LoadCustomLocations();
IsDataLoaded = true;
}
private LocationGroup LoadCustomLocations()
{
string dataFromAppSettings;
LocationGroup data;
if (IsolatedStorageSettings.ApplicationSettings.TryGetValue(CustomKey, out dataFromAppSettings))
{
data = JsonConvert.DeserializeObject<LocationGroup>(dataFromAppSettings);
}
else
{
data = new LocationGroup();
}
return data;
}
So, can anyone help ?
In the case you described on your comment I think you set the DataContext to your Items. When you create a new Items-List the DataContext will be lost. So you have to reset the DataContext to the new loaded Items-List
In the code behind of my Silverlight application, I have the need to re-populate the TreeView and then make a specific TreeViewItem selected.
The code itself is pretty simple, here it is (i'll trim and pseudo-code-ify it to make it as short as possible)
private void Button_Click()
{
Guid idToSelect = TellMeWhatToSelect();
List<myObject> myDataList = myObjectRepository.RetrieveData().ToList();
myTreeView.Items.Clear();
foreach(myObject o in myDataList)
{
myTreeView.Items.Add(new TreeViewItem() { Content = o.DataField, Tag = o.Id });
}
myTreeView.Items.First(o => ((Guid)(o as TreeViewItem).Tag).Equals(idToSelect)).IsSelected = true;
}
That's basically it: i'm reading some data into myDataList, then i cycle through it and create as many TreeViewItems as needed in order to display the data.
Problem is, myTreeView.SelectedItem is null at the end of this, and SelectionChanged event isn't triggered. I would think that, since Items collection has been cleared and re-filled, switching IsSelected on one of the items would act like clicking, but it seems it doesn't).
Oddly enough (for me at least), issuing myTreeView.Items.First().IsSelected = true; by itself (that is, calling a method with that single line of code inside) works as expected: SelectedItem is there and all events are fired appropriateyl.
What's wrong with my code and/or what am I missing ? Looks like cleaning items up kind of breaks something.
I'm fairly sure others have had similar issues, but a bunch of searches I tried didn't help (most of the info & questions I came up with are WPF-related).
Thanks for your time, I'll provide more info if needed. Also, sorry for the wall of text.
UPDATE
Modifying code like this, now the method works as expected.
private void Button_Click()
{
Guid idToSelect = TellMeWhatToSelect();
List<myObject> myDataList = myObjectRepository.RetrieveData().ToList();
myTreeView.Items.Clear();
foreach(myObject o in myDataList)
{
myTreeView.Items.Add(new TreeViewItem() { Content = o.DataField, Tag = o.Id });
}
Dispatcher.BeginInvoke(()=>
{
myTreeView.Items.First(o => ((Guid)(o as TreeViewItem).Tag).Equals(idToSelect)).IsSelected = true;
});
}
Set property IsSelected inside of Dispatcher.BeginInvoke.
I had the same problem a while ago, I've solved by calling the UpdateLayout method from the treeview before setting the TreeViewItem as selected.Like this:
myTreeView.UpdateLayout();
myTreeView.Items.First(o => ((Guid)(o as TreeViewItem).Tag).Equals(idToSelect)).IsSelected = true;
I'm using a PagedCollectionView to bind an ObservableCollection<T> to a DataGrid in my Silverlight app. In this case, the source collection can incur an unbounded number of updates during its lifespan. It seems, however, that if I'm using a PropertyGroupDescription to group the elements in the DataGrid then I need to reapply that grouping using PagedCollectionView.GroupDescriptions.Add(...) every time the source collection is updated with an element that doesn't fit into an existing grouping. Is there any way to make the groupings refresh/recalculate automatically?
public ObservableCollection<DataItem> DataItems
{
get { return _dataItems; }
private set { _dataItems = value; }
}
public PagedCollectionView ItemsView
{
get { return _itemsView; }
private set { _itemsView = value; }
}
public MyGridControl()
{
// Initialize data members
DataItems = new ObservableCollection<DataItem>();
ItemsView = new PagedCollectionView(GridDataItems);
Loaded += new RoutedEventHandler(MyGridControl_Loaded);
}
private void MyGridControl_Loaded(object sender, RoutedEventArgs e)
{
_myGrid.ItemsSource = ItemsView;
}
public void RegisterDataItem(DataItem item)
{
DataItems.Add(item);
/* To get grouping to work like I want, I have to add this:
* ItemsView.GroupDescriptions.Clear();
* ItemsView.GroupDescriptions.Add(new PropertyGroupDescription("GroupName"));
*/
}
public void UnregisterError(DataItem item)
{
DataItem.Remove(item);
}
Thanks, and let me know if I can provide any additional information.
So, as far as I can tell, the grouping operation appears to be a once-off operation that is performed on the data as it exists at the time that the PagedCollectionView.GroupDescriptions collection is manipulated. It seems that the desired groupings do indeed need to be re-applied when the source collection is changed.
I did find one alternative approach for my specific case. Since I'm using an ObservableCollection for the source collection of the view, I can wire up something to the CollectionChanged event of that source collection wherein the PagedCollectionView.GroupDescriptions collection gets cleared and the desired GroupDescription is then reapplied. This doesn't seem totally compliant with good OO practices, nor is it usable if the source collection for the view doesn't implement INotifyCollectionChanged.
I'll leave this open a little longer in case anyone can offer another approach, otherwise I'll just concede.
Thanks again.
Looking at the code in Reflector, the Groupings are correctly processed but if you use view in a DataGrid with IsReadOnly == true you won't see the groupings reflected while changing the source collection.