WPF Dictionary Binding by key does not work - binding

I really don't get it.
I created a viewmodel-class, which contains two objects, like this one:
public class MyViewModel{
public MyFirstObject FirstObject {get;set;}
public MySecondObject SecondObject {get;set;}
...
// Constructors and so on...
}
The FirstObject looks like this:
public class MyFirstObject {
public int Id {get;set;}
...
// Constructors and so on...
}
}
And MySecondObject contains a Dictionary:
public class MySecondObject{
public Dictionary<int, string> MyDict {get;set;}
...
// Constructors and so on...
}
}
What I want to do now, is to get the dictionarys value for a key, which is a refence to MyFirstObject.Id - like this:
mySecondObject.myDict[myFirstObject.Id]
Doing this in C# is easy, but now I want to do this in xaml.
I created a UserControl and set the DataContext to a MyViewModel-reference.
After that, I tried this:
<TextBox Text={Binding SecondObject[FirstObject.Id]} ../>
Unfortunately this is not working, because the key 'FirstObject.Id' can not be found.
Has anyone an idea how to fix that?
Thanks a lot!
CodeCannibal

Don't do this in XAML. Just create a new property on your viewmodel which encapsulates this logic.
You can also delegate the PropertyChangedEvent to propagate your value changes:
public class MyViewModel : INotifyPropertyChanged {
private MyFirstObject firstObject;
public MyFirstObject FirstObject
{
get { return firstObject; }
set { firstObject = value; PropertyChanged("MyDictValue"); }
}
private MySecondObject secondObject;
public MySecondObject SecondObject
{
get { return secondObject; }
set
{
secondObject = value; PropertyChanged("MyDictValue");
// you can subscribe here also for your MyDict changes if it is an
// observable and you can call PropertyChanged("MyDictValue");
// in the change event etc
}
}
public string MyDictValue
{
get { return SecondObject.MyDict[FirstObject.Id]; }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And bind to your new property:
<TextBox Text={Binding MyDictValue} ../>
With this approach you will have a place inside the MyDictValue to add additional logic like handling if FirstObject or SecondObject is null etc.

Related

Calling variable MvxCommand(s) from custom CollectionViewCell

I've been banging my head against this for two days now working with mvvmcross, and having never worked with iOS before I think there's just something I don't understand.
I created my main menu with a UICollectionView in a two-column 3-row grid, each item representing a different location the user can go to on tap. I can override "ItemSelected" from the UICollectionViewSource, but I can't access the actual ViewModel without passing a reference of it into the source on creation....which doesnt feel like the right way to do it to me.
[MvxRootPresentation(WrapInNavigationController = true)]
public partial class MainPageView : MvxViewController
{
private MenuCollectionSource _menuCollectionSource;
List<MainMenuItem> menuItems;
public MainPageViewModel VM
{
get { return DataContext as MainPageViewModel; }
}
private void SetupMenuCollectionView()
{
......
collectionView.RegisterNibForCell(MainMenuCollectionViewCell.Nib, MainMenuCollectionViewCell.Key);
MainMenuItem.Init(menuItems);
_menuCollectionSource = new MenuCollectionSource(collectionView, MainMenuCollectionViewCell.Key, menuItems);
_menuCollectionSource.VM = VM; <----doesnt seem right.
collectionView.Source = _menuCollectionSource;
public class MenuCollectionSource : MvxCollectionViewSource
{
private UICollectionView _collectionView;
public List<MainMenuItem> Items { get; set; }
private MainPageViewModel _vm;
public MainPageViewModel VM
{
get { return _vm; }
set { _vm = value; }
}
}
With this method I can override ItemSelected in the ViewSource, and the do something like
( Cell is touched ->
Depending on cell enum/cell# - >
vm.NavigateToCorrectPage())
While this method works, I don't think its the correct way to handle this situation.
So then my next thought was to bind the source like like...(may not be 100%, trying to remember in my head)
set.CreateBinding(_menuCollectionSource) .For(s => s.SelectedCommand) .To(vm => vm.NavigateTo) .CommandParameter(_menuCollectionSource.SelectedItem)
But no matter what I tried, the passed param was always null as if the selected item was never set or the command was being called before it was set.
My CollectionViewCell class is pretty basic
public enum NavigationLocation
{
Search Database,
Lists,
etc....
}
public partial class MainMenuCollectionViewCell : MvxCollectionViewCell
{
public static readonly NSString Key = new NSString("MainMenuCollectionViewCell");
public static readonly UINib Nib;
public string MainMenuLabel
{ get { return mainMenuLabel.Text; } }
public int MainMenuIndexNumber
{ get; set; }
protected MainMenuCollectionViewCell(IntPtr handle) : base(handle)
{
}
static MainMenuCollectionViewCell()
{
Nib = UINib.FromName("MainMenuCollectionViewCell", NSBundle.MainBundle);
}
public static MainMenuCollectionViewCell Create()
{
NSArray topLevelObjects = NSBundle.MainBundle.LoadNib("MainMenuCollectionViewCell", null, null);
MainMenuCollectionViewCell cell = Runtime.GetNSObject(topLevelObjects.ValueAt(0)) as MainMenuCollectionViewCell;
return cell;
}
internal void BindData(string label, string iconBundleName)
{
mainMenuLabel.Text = label;
mainMenuImage.Image = UIImage.FromBundle(iconBundleName);
}
}
No binding I've tried in the cell class has actually worked, even adding a UITapGestureRecognizer on creation caused a crash on actual tap. I've run out of ideas, does anyone know what I'm not understanding or missing to actually implement
( Cell is touched ->
GetCellMenuType - >
CallCorrectCommandFromViewModel)
Thank you
Use EventHandler SelectedItemChanged from MvxBaseCollectionViewSource
View
set.Bind(yourCollectionViewSource).For(s => s.SelectionChangedCommand).To(vm => vm.CollectionItemSelected);
ViewModel
public ICommand CollectionItemSelected => new MvxCommand<ItemViewModel>((selectedItem) => { });

How to pass Dbset object as a parameter to any user define function in entity framework code first?

I want to pass Dbset Object to my method.
This is my Method:
public static class MyClass
{
public static Floor ReturnObject(this DbSet<Floor> floor, int Id)
{
var context = floor.passContext() as MyDBContext;
var data = context.floors.Where(a =>a.Id == Id).FirstOrDefault();
return ad;
}
}
public class MyDBContext: DbContext
{
public DbSet<Floor> floors { get; set; }
}
public static DbContext passContext<TEntity>(this DbSet<TEntity> dbSet)
where TEntity : class
{
//code....
}
Now i want to use my method of static class MyClass that is ReturnObject.
I want to use ReturnObject method in my Controller.
I know i can declare it as private member like this in my controller:
This is my controller:
public class MyController
{
private DbSet<Floor> floor;//but is this a good way???
public ActionResult Index(int Id)
{
var data=MyClass.ReturnObject(????,Id)//what should come in place of question mark??
}
}
How should i pass my First Parameter to ReturnObject Method???
Change your ReturnObject method. It should only take Id as parameter and filter data than return object of Floor.
public static Floor ReturnObject(int Id)
{
using(MyDBContext context = new MyDBContext())
{
var data = context.floors.Where(a =>a.Id == Id).FirstOrDefault();
return ad;
}
}
And when you call it than only pass id as paramter
var data=MyClass.ReturnObject(Id);
This will return object of floor which will be stored in data.

MvvmCross & MvxAdapter & Polymorphic Types with custom controls

I am currently trying to implement my own version of the polymorphic types demo that is located here:
https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/Working%20With%20Collections
And I have it working as the demo shows. However, I am looking to extend that demo to have more complex controls inside of the MvxListView. I am wanting to have each of the list items control a fragment that has a View and a core ViewModel for additional processing.
I am unsure of the correct way of implementing this.
The code that I am using to create the custom view is this:
protected override View GetBindableView(View convertView, Object source, Int32 templateId)
{
var listItem = (TodayPanel) source;
if (listItem != null)
templateId = (Int32) typeof (Resource.Layout).GetField(listItem.View).GetValue(null);
return base.GetBindableView(convertView, source, templateId);
}
As always, it's probably something simple that I am missing, but any help would be appreciated.
Thanks!
I hate it when this happens, but after posting my question, I stepped away from the computer for a little bit and started to do something else. At that point, everything clicked into place. Stuart, in response to your question, the TodayPanel was NOT an MvxModelView, and therein was the crux of the problem. What I was doing was passing a list of TodayPanels into the listview, which was an SQLite entity object and not an MvxModelView object.
For others that might be struggling with this, I am going to post my solution here.
So here is what I ended up doing. I first created a class for each of the TodayPanel entity objects that inherited from an abstract base class that inherited from MvxModelView.
public abstract class TodayBaseViewModel : MvxViewModel
{
protected TodayViewModel TodayViewModel { get; set; }
protected IDataService DataService { get; set; }
public String Name { get; set; }
public String Title { get; set; }
public Boolean CanHide { get; set; }
public Boolean Visible { get; set; }
public Int32 SortOrder { get; set; }
public String View { get; set; }
protected abstract void SetEventHandlers();
protected BaseViewModel(IDataService dataService)
{
DataService = dataService;
}
public void Init(TodayViewModel todayViewModel)
{
TodayViewModel = todayViewModel;
SetEventHandlers();
}
}
I made it abstract as I wanted 0 or more event handlers to be attached in the final class. which is done through the abstract SetEventHandlers() method:
public class CoachSaysViewModel : TodayBaseViewModel
{
public CoachSaysViewModel(IDataService dataService)
: base(dataService)
{
}
protected override void SetEventHandlers()
{
TodayViewModel.ConnectionUpdated += TodayViewModelConnectionUpdated;
TodayViewModel.NewActivityReceived += TodayViewModelNewActivityReceived;
}
protected void TodayViewModelNewActivityReceived(Object sender, EventArgs.ActivityReceivedEventArgs e)
{
}
protected void TodayViewModelConnectionUpdated(Object sender, EventArgs.ConnectionUpdatedEventArgs e)
{
}
}
Then I created an extension method that converts the TodayPanel entity to one of the classes that inherits from TodayBaseViewModel.
public static BaseViewModel ToBaseViewModel(this TodayPanel todayPanel, TodayViewModel todayViewModel)
{
BaseViewModel model = null;
switch (todayPanel.View)
{
case "Today_QuickView":
model = Mvx.IocConstruct<QuickViewViewModel>();
break;
case "Today_CoachSays":
model = Mvx.IocConstruct<CoachSaysViewModel>();
break;
}
if (model == null)
return null;
model.CanHide = todayPanel.CanHide;
model.Name = todayPanel.Name;
model.SortOrder = todayPanel.SortOrder;
model.Title = todayPanel.Title;
model.View = todayPanel.View;
model.Visible = todayPanel.Visible;
model.Init(todayViewModel);
return model;
}
That then allowed me to create a list of MvxViewModels that are then bound to the MvxListView and hence are allowed to do the additional processing that I am wanting to do.
I'm sure that there are some improvements that I can do to the end result, and if you see anything feel free to point it out. :)

Serialize IList property on model when passed into Html.ActionLink

I'm trying to generate an Html.ActionLink with the following viewmodel:
public class SearchModel
{
public string KeyWords {get;set;}
public IList<string> Categories {get;set;}
}
To generate my link I use the following call:
#Html.ActionLink("Index", "Search", Model)
Where Model is an instance of the SearchModel
The link generated is something like this:
http://www.test.com/search/index?keywords=bla&categories=System.Collections.Generic.List
Because it obviously is only calling the ToString method on every property.
What I would like to see generate is this:
http://www.test.com/search/index?keywords=bla&categories=Cat1&categories=Cat2
Is there any way I can achieve this by using Html.ActionLink
In MVC 3 you're just out of luck because the route values are stored in a RouteValueDictionary that as the name implies uses a Dictionary internally which makes it not possible to have multiple values associated to a single key. The route values should probably be stored in a NameValueCollection to support the same behavior as the query string.
However, if you can impose some constraints on the categories names and you're able to support a query string in the format:
http://www.test.com/search/index?keywords=bla&categories=Cat1|Cat2
then you could theoretically plug it into Html.ActionLink since MVC uses TypeDescriptor which in turn is extensible at runtime. The following code is presented to demonstrate it's possible, but I would not recommend it to be used, at least without further refactoring.
Having said that, you would need to start by associating a custom type description provider:
[TypeDescriptionProvider(typeof(SearchModelTypeDescriptionProvider))]
public class SearchModel
{
public string KeyWords { get; set; }
public IList<string> Categories { get; set; }
}
The implementation for the provider and the custom descriptor that overrides the property descriptor for the Categories property:
class SearchModelTypeDescriptionProvider : TypeDescriptionProvider
{
public override ICustomTypeDescriptor GetTypeDescriptor(
Type objectType, object instance)
{
var searchModel = instance as SearchModel;
if (searchModel != null)
{
var properties = new List<PropertyDescriptor>();
properties.Add(TypeDescriptor.CreateProperty(
objectType, "KeyWords", typeof(string)));
properties.Add(new ListPropertyDescriptor("Categories"));
return new SearchModelTypeDescriptor(properties.ToArray());
}
return base.GetTypeDescriptor(objectType, instance);
}
}
class SearchModelTypeDescriptor : CustomTypeDescriptor
{
public SearchModelTypeDescriptor(PropertyDescriptor[] properties)
{
this.Properties = properties;
}
public PropertyDescriptor[] Properties { get; set; }
public override PropertyDescriptorCollection GetProperties()
{
return new PropertyDescriptorCollection(this.Properties);
}
}
Then we would need the custom property descriptor to be able to return a custom value in GetValue which is called internally by MVC:
class ListPropertyDescriptor : PropertyDescriptor
{
public ListPropertyDescriptor(string name)
: base(name, new Attribute[] { }) { }
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { throw new NotImplementedException(); }
}
public override object GetValue(object component)
{
var property = component.GetType().GetProperty(this.Name);
var list = (IList<string>)property.GetValue(component, null);
return string.Join("|", list);
}
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType
{
get { throw new NotImplementedException(); }
}
public override void ResetValue(object component) { }
public override void SetValue(object component, object value) { }
public override bool ShouldSerializeValue(object component)
{
throw new NotImplementedException();
}
}
And finally to prove that it works a sample application that mimics the MVC route values creation:
static void Main(string[] args)
{
var model = new SearchModel { KeyWords = "overengineering" };
model.Categories = new List<string> { "1", "2", "3" };
var properties = TypeDescriptor.GetProperties(model);
var dictionary = new Dictionary<string, object>();
foreach (PropertyDescriptor p in properties)
{
dictionary.Add(p.Name, p.GetValue(model));
}
// Prints: KeyWords, Categories
Console.WriteLine(string.Join(", ", dictionary.Keys));
// Prints: overengineering, 1|2|3
Console.WriteLine(string.Join(", ", dictionary.Values));
}
Damn, this is probably the longest answer I ever give here at SO.
with linq of course...
string.Join("", Model.Categories.Select(c=>"&categories="+c))

How to configure ObjectFactory to call parameterized constructor structuremap

PLEASE: If my question isn't clear, please tell me and I'll try to rephrase it
I need [Default Constructor] in LogOnModel, so it can't be removed.
LoadModel+ModelFactory and LogOnModel are physically in different files in different projects AND project2 has reference to project1 and NOT vice versa.
1 - Let say that
type=typeof(LogOnModel). When ObjectFactory.GetInstance(t) is called I want it to call the
parameterized constructor of LogOnModel and pass it the #params
2 - If I'll add to the parameterized constructor of LogOnModel another parameter,for example
public LogOnModel(string param, IPageService pageService)
so ObjectFacytory should call this constructor without any problems
How to configure/initiate ObjectFactory, so this will work?
Thank you
EDITED
//Project1/file1.cs
public void LoadModel(Type type, string param)
{
var factory = new ModelFactory();
var model = factory.Get(type, **param**);
}
public class ModelFactory : IModelFactory
{
public PageModel Get(Type t, **string param**)
{
//NOW I NEED SOMEHOW TO PASS **param** TO EVERY INSTANCE THAT INHERITS FROM **PageModel**
return ObjectFactory.GetInstance(t) as PageModel;
}
}
//Project2/file2.cs
public class LogOnModel : PageModel
{
public LogOnModel()
{
}
public LogOnModel(string param)
{
}
}
public class Model2 : PageModel
{
public LogOnModel()
{
}
public LogOnModel(string param)
{
}
}
public class Model3 : PageModel
{
public LogOnModel()
{
}
public LogOnModel(string param)
{
}
}
StructureMap will use the constructor with the most parameters by default, so that part is easy. Then you just need to configure the value of param like so:
ObjectFactory.Initialize(i => {
i.For<LogOnModel>().Use<LogOnModel>();
});
When you call the container, use the with method to pass in your parameter:
return ObjectFactory.With("param").EqualTo(param).GetInstance(t) as PageModel;

Resources