How to create single observable value? - tornadofx

I've got a top-level Controller object that is holding a reference to three objects (MyObject). I'd like to position these precisely on the page at any time, but I would like each object to also be editable, and I'm not really sure how to do that.
So far, I've got a class that extends ItemFragment and displays my individual items, like this:
class MyObjectFragment(o: MyObject) : ItemFragment<MyObject>() {
override val root = hbox {
...
}
}
Meanwhile, I have a top-level View with a reference to my controller, like this:
class TopLevelView : View() {
val controller = TopLevelController()
override val root = hbox {
add(MyObjectFragment(controller.myObject1))
...
add(MyObjectFragment(controller.myObject2))
...
add(MyObjectFragment(controller.myObject3))
}
}
And right now, all I have for the top level controller is this:
class TopLevelController() : Controller() {
val myObject1 = MyObject()
val myObject2 = MyObject()
val myObject3 = MyObject()
}
I'm trying to figure out what I need to do to wrap these objects as observable values. My first attempt was to add something like this to the init method of MyObjectFragment:
class MyObjectFragment(o: MyObject) : ItemFragment<MyObject>() {
init {
itemProperty.bind(o)
}
...
}
However, that method only takes an ObservableValue<MyObject>. What is the best way to get that to tie all of this together?

You can create an observable list of your objects like so:
class TopLevelController() : Controller() {
val myObjects = FXCollections.observableArrayList<MyObject>(MyObject(), MyObject(), MyObject())
}
Then in your TopLevelView, you can bind this list to a layout node's children property, and inflate the proper Fragment for each object:
class TopLevelView : View() {
val controller = TopLevelController()
override val root = hbox {
bindChildren(controller.myObjects) {
MyObjectFragment(it).root
}
}
}

I'll admit your requirements seem a little vague to me without more information. It'd be helpful to know more of what you want from the item fragments. Are they going to be in a list view or table? Or something more dynamic? And how exactly would they be editing? Would there be a save button or would you expect any input to commit those changes immediately?
import tornadofx.*
class MyObject() {
//val someProperty = SimpleObjectProperty<Something>()
//var some by someProperty
}
class MyObjectModel(myObject: MyObject? = null) : ItemViewModel<MyObject>(myObject) {
//val someBinding = bind(MyObject::someProperty)
}
class MyObjectFragment : ItemFragment<MyObject>() {
val model: MyObjectModel by inject()
override val root = hbox {
label("This is an MyObject Fragment")
//you would bind some control to the model binding in here. For example:
//textfield(model.someBinding)
}
init {
itemProperty.value = model.item
model.bindTo(this)
}
}
class TopLevelController : Controller() {
val myObject1 = MyObject()
val myObject2 = MyObject()
val myObject3 = MyObject()
}
class TopLevelView : View() {
val controller: TopLevelController by inject()
override val root = vbox {
add(setUpObjectFragment(controller.myObject1))
add(setUpObjectFragment(controller.myObject2))
add(setUpObjectFragment(controller.myObject3))
}
fun setUpObjectFragment(obj: MyObject) = find<MyObjectFragment>(Scope(MyObjectModel(obj)))
}
It also seems you're new and are missing a lot of key concepts to utilize from TornadoFX. Using find for instance, is really important for Fragments and Views so they have the proper life cycle.
Then there's Scopes which help with being able to call certain Components with injection.
And finally, there are Model classes, most importantly ItemViewModel which gives you the most functionality with editing, like being able to rollback and commit changes as well as to mark properties as required and add validate filters.
If this isn't a satisfactory solution, please give us more information on what you want, as I might be able to provide a more elegant and concise solution. If these concepts are confusing, please look at Edvin's guide.

Related

Holding classes in Dictionary

I have a question about holding a class in dictionary.So I am working on a project about a university.There are more than one faculty names.When user types a faculty name,I am directing user to appropriate faculty class with using context.call
So in here,if user enters show me computer engineering,user directed to the ShowComp class.
But using if-else makes code really unreadable.I thought that I can put these keywords to dictionary
But this time context.Call gives an error about the value type.What should I put dictionary value type.I couldn't figure it out.Can anyone help me please?
Since Dialogs inherit from IDialog<object>, you can put that in the dictionary:
private readonly Dictionary<string, IDialog<object>> options
= new Dictionary<string, IDialog<object>>
{ { "computer", new ShowComp() }, { "law", new ShowLaw() } };
public async Task GetFacilities(IDialogContext context, LuisResult result)
{
var entity = result.Entities.FirstOrDefault(e => options.ContainsKey(e.Entity));
if (entity != null)
{
IDialog<object> dialog = null;
if (options.TryGetValue(entity.Entity, out dialog))
{
context.Call(dialog, this.AfterResume);
}
}
}
Do it like this:
static Dictionary<String, Type> FACULTY_CLASS_MAP;
/**
* faculty class mapping.
*/
FACULTY_CLASS_MAP= new Dictionary<String, Type>
{
{ "Text", typeof(FacultyClass) }
}

Prism - moving data between viewmodels

I'm struggling to find the best implementation.
I'm using Prism and I have a View (ParentView), which has a small region within it. Depending on the item in a ddl, another smaller view (ChildView) gets injected into the region of the ParentView.
The ChildView will just have some properties which I would like to access from the ParentView.
So I realize I can use a Publish/Subscribe method to move data between viewmodels, but the issue is I have nothing to hang the Publish on. The view is made up of TextBoxes and no event triggers. The ChildView can be vastly different based on the selection of the ddl. I like the clean separation of each ChildView being it's own view injected inside the ParentView.
What is the best way to achieve this?
One solution can be to implement the interface INavigationAware in your viewmodels. After that you can use the methods onNavigatedFrom(), onNavigatedTo() and onNavigatingTo() to register your event.
EDIT:
If you want launch the event when a field in the child is changed you can do something like this:
private string _yourField;
public string YourField
{
get { return _yourField; }
set { SetProperty(ref _yourField, value);
//Here you can launch the event
}
}
In this case when YourField change the event is launched.
I tried a few implementations, but the one that worked was creating a singleton instance of the ChildView (childviewmodel) and then gaining access to the properties through the instance. It may not be pretty, but it works.
private static ChildViewModel _instance = new ChildViewModel ();
public static ChildViewModel Instance { get { return _instance; } }
#region Properties
private ChildModel _childModel= new ChildModel ();
public ChildModel _childModel
{
get { return _instance._childModel; }
set
{
SetProperty(ref _instance._childModel, value);
}
}
private string _childProperty1;
public string ChildProperty1
{
get { return _childProperty1; }
set
{
SetProperty(ref _childProperty1, value);
ChildModel.ChildProperty1= _childProperty1;
}
}
In reality - there were many childproperties. I only listed one for demo. And then I call it in ParentView
var _instance = ChildViewModel.Instance;
var _cm = _instance.ChildModel;
_parentModel = new ParentModel
{
Property1= ParentViewProperty1,
Property2= _cm.ChildProperty1,
};
Hope that helps someone else.

How to share ShowViewModel class in ViewModel

I am developing a Xamarin mobile app using MVVM Cross. There are two ViewModels which are doing same thing i.e. showing a dialog using the code below:
var register = await UserDialogHelper.RaiseNotRegisteredAsync (UserDialogs);
if (register) {
ShowViewModel<WebViewModel> (new
{
url = Urls.RegisterPage,
title = "Register",
});
}
I tried moving this code to static class but unable to resolve ShowViewModel. Can anyone suggest how to resolve ShowViewModel in non-viewmodel class?
When you have methods or properties to share between the same viewmoels. You can just implement a base viewmodel. And the other ones just inherit from this base viewmodel. Like following example:
public abstract class MyBaseViewModel : MvxViewModel
{
public void MyMethod()
{
// Your code
var register = await UserDialogHelper.RaiseNotRegisteredAsync (UserDialogs);
if (register) {
ShowViewModel<WebViewModel> (new
{
url = Urls.RegisterPage,
title = "Register",
});
}
}
}
And then your viewmodels look like this:
public class MyFirstViewModel : MyBaseViewModel
{
}
Inside this MyFirstViewModel you can call the base method MyMethod. And so on...
Edit
If you want to navigate from outside a view/viewmodel: Look at this answer from Stuart or the answer here from #SergioZgz
ShowViewModel comes from MvxNavigatingObject if your class doesn't inherited it you cannot use it.
You could something like this in a class than is not a MvxViewModel:
var viewDispatcher = Mvx.Resolve<IMvxViewDispatcher>();
viewDispatcher.ShowViewModel(new MvxViewModelRequest(
vmtype,
parameterBundle,
presentationBundle,
requestedBy));
But I think that answer from Joehl is the correcty way :)

How to create dynamic, multiple partial views using repository pattern in MVC

I am trying to have a general home page that depending on the parameter passed to the control, different content (modules) will be displayed.
For example, a user may select Kentucky from the menu and the id for Kentucky is 1. The home controller gets the id (1) and determines the possible modules for that
state (a simple db call.) Perhaps there is an announcements module and a contacts module for the state. An announcements module could have several items but it's only one module. There would be a partial view for each type of module.
Here is the basic setup I have.
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements
{
private int _ID = -1;
private int _MenuItemID = -1;
private int _ModuleID = -1;
private string _Description = string.Empty;
//get set props ...
}
In my home controller...
public class HomeController : Controller
{
private IModuleRepository modRepository;
public HomeController(IModuleRepository modRepository)
{
this.modRepository = modRepository;
}
public ViewResult Item(string ItemID)
{
//returns a list of menuitemmodules for the page. This gives me the Src or name of each
//module on the page, i.e. Announcements, news, contacts, etc.
var modules = modRepository.GetMenuItemModules(Convert.ToInt32(ItemID));
return View(modules);
}
}
I have tried several different models to return but I always run up against some contstraint. If I pass the menuitemmodules to my Item.aspx, then I can do something like this:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, a); //needs an announcement object though
}
That makes it somewhat dynamic because I have the Src which would basically be something like "Announcements" and I can just create an announcements.ascx partial to process the module. But I have found it difficult to pass my menuitemmodule and an announcements entity as well.
I have also messed around with passing a more complex object and then testing every Src that comes through with an If statement. This would make scaling difficult in the future as I increase the number of possible modules in the app.
How can I solve my problem? I hope I have provided enough info. I like the basic idea here - http://www.mikesdotnetting.com/Article/105/ASP.NET-MVC-Partial-Views-and-Strongly-Typed-Custom-ViewModels but that seems to only work for static modules on a page.
I did try a composite view model called ModuleViewModel. Here is that attempt:
public class ModuleViewModel
{
public IList<Announcements> announcements { get; set; }
public IList<MenuItemModule> mods { get; set; }
}
If I pass that model to the Item.aspx I can do something like this (but I must be doing something wrong because something doesn't look right.)
foreach (var mod in Model)
{
if (mod.announcements.Count > 0)
{
Html.RenderPartial("Announcements", mod.announcements);
}
}
Once again, scalability is going to haunt me. I would like to have something like this on item page:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, mod);
}
That would the correct partial view and pass it the correct model.
Create Module classes that derive from a common Module base class:
public class AnnouncementsModule : Module
{
}
public class ContactsModule : Module
{
}
In controller:
Create your various modules and put them into your overall view module (here it has a property called Modules that is an array of Module:
var viewModel = new ComplexViewModel
{
Modules = new []
{
new ContactsModule(),
new AnnouncementsModule()
}
};
return View(viewModule);
In view:
#Html.DisplayFor(x => x.Modules);
Create the partial views for each Type of Module in the appropriate 'Shared` folder. (Run it without creating them and it will show you an exception with the locations where it's looking for them).
After messing around with this for over a week, I finally managed to figure out how MVC can do what I want dynamically. I decided to post my solution for others that are new to MVC. Hopefully, the following will clear up the misunderstandings I had (although, at this point in my understanding of MVC, I cannot say this is the best approach.)
I will include the previous code snips and modifications for clarity:
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements : MenuItemModule
{
private int _ID = -1;
private string _Description = string.Empty;
//get set props ...
}
I also added another class:
public class AnnouncementModule : MenuItemModule
{
private IList<Announcements> _Announcements;
//get set prop
}
...and I created a model for the view
public class HomeItemViewModel
{
public MenuItemModule[] MenuItemModules { get; set; } //collection of menuitemmodules
}
In my home controller...
var menuItemModules = modRepository.GetMenuItemModules(ItemID);
if (menuItemModules.Count > 0)
{
AnnouncementModule aMod;
MenuItemModule[] mods = new MenuItemModule[menuItemModules.Count()];
int i = 0;
//loop through each MenuItemModule assign to the appropriate model
foreach (MenuItemModule mod in menuItemModules)
{
if (mod.Src == "Announcements")
{
aMod = new AnnouncementModule();
aMod.Announcements = modRepository.GetAnnouncements(mod.ModuleID);
//now add this to the menuitemmodule collection
mods[i] = aMod;
}
if (mod.Src == "Contacts")
{
//...
}
i++;
}
}
var viewModel = new HomeItemViewModel
{
MenuItemModules = mods
};
return View(viewModel);
Then I used the suggestion to use DisplayFor in the view. The view is strongly typed to HomeItemViewModel.
<%: Html.DisplayFor(m => m.MenuItemModules) %>
This iterates through the collection and based on the type, it will call that template. In this example, it calls AnnouncementModule.ascx which is strongly typed to AnnouncementModule.
foreach (var a in Model.Announcements)
{
//a.Description will give us the description of the announcement
}
I realize there are slicker ways to code the controller, and I plan on refactoring, but this skeleton should provide the basics to solve the question I posted.

LINQ-To-Sharepoint Multiple content types for a single list

I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.

Resources