Proper way to do dialogs using Prism and UWP - mvvm

I'm not sure if I'm thinking about this in the correct way. I have a list of objects, and I would like the user to be able to edit and view the properties of a specified object. My initial thought is great, I'll pop up a dialog that has textboxes and let the user edit to their heart's content until they press either Ok or Cancel.
I'm on UWP, and using Prism for all of my MVVM needs. It's taken me a while, but I understand creating Views and their associated ViewModels, commands, etc. So far, I think I've done a good job keeping the view logic and business logic separated.
I've searched, but haven't found how to show a dialog in a way that follows MVVM principles. The two things that seem to be coming up the most are to use Interaction requests (which don't appear to exist using Prism on UWP), and creating a custom Content Dialog and showing it by calling ShowAsync in a method in the parent view's associated ViewModel (which seems to be counter to MVVM principles).
So, how do I either show a dialog that is defined using XAML and has an associated ViewModel (preferable since it is similar to what I'm familiar with), or another way I can tackle this problem?

Using MVVM the proper place for opening a dialog is in the ViewModel.
Usually I do something like this in your scenario:
Create an interface for showing dialogs:
public interface IWindowService
{
void OpenViewModelInWindow(ViewModelBase vm, string title, bool resizeable = true);
void CloseViewModelInWindow(ViewModelBase vm);
}
Implement this interface in UI layer:
public class WindowService : IWindowService
{
private List<Window> _windows = new List<Window>();
public void OpenViewModelInWindow(ViewModelBase vm, string title, bool resizeable = true)
{
var window = new Window
{
Title = title,
Content = vm,
Owner = Application.Current.MainWindow,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
ShowInTaskbar = false,
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = resizeable ? ResizeMode.CanResize : ResizeMode.NoResize
};
_windows.Add(window);
window.ShowDialog();
}
public void CloseViewModelInWindow(ViewModelBase vm)
{
_windows.Single(w => w.Content == vm).Close();
}
}
In your App.xaml you need to define DataTemplates so that when you set the Content property of the window the corresponding View created in the window.
<DataTemplate DataType="{x:Type viewModel:AViewModel}">
<views:AUserControl />
</DataTemplate>
Then you can use the IWindowService from the ViewModel, you should inject it by constructor injection.
This way you are not referencing framework specific classes directly from the ViewModel. ViewModel has a reference only to an IWindowService. This has a benefit also when you want ot write unit test for a viewmodel. You can mock this service so that when the unit test is running it should not open a dialog.

Related

Nested Variables in Unity Inspector

I was wondering if anyone knew how to make nested variables inside the unity inspector with a script, kind of like this:
Doing so does require knowledge of UnityEditor and not only (as you say ... nested variables) can give you many other control options in the inspector. To do this, I created a sample code called MenuManager. As you can see this code:
public class MenuManager : MonoBehaviour
{
public bool variable1;
public float nestedVariable;
//...
}
Unity itself does not provide any attributes such as [Range] or [Header] for such a request, and to do this you need to define a CustomEditor for the class but Before to do that, you need to create a folder similar to the photo with name of Editor and put it in the Assets folder. Then create another script with name of MenuEditor (for example here ..) and put it in a Editor folder.
Now open the MenuEditor code. Inherit it from the Editor class. Editor class is base class for editing inspector and more. It will give you many override methods with access to the features inside the unity editor. and make sure it has two Attributes Custom Editor as well as CanEditMultipleObjects.
[CustomEditor(typeof(MenuManager))]
[CanEditMultipleObjects]
public class MenuEditor : Editor
{
//..
}
This code gives you access to the MenuManager script. According to the following code, I coded a Nested variable to the first one.
[CustomEditor(typeof(MenuManager))]
[CanEditMultipleObjects]
public class MenuEditor : Editor
{
public override void OnInspectorGUI()
{
var myMenu = target as MenuManager; // target is script reference that we want to manipulate it
myMenu.variable1 = EditorGUILayout.Toggle("Variable 1", myMenu.variable1); // show first variable on inspector
GUI.enabled = myMenu.variable1; // access to second variable depend of first
myMenu.nestedVariable =EditorGUILayout.Toggle("Nested Variable", myMenu.nestedVariable);
GUI.enabled = true;
}
}
After finishing the work, you can access the nested variable only by setting first one to true.
Remember after doing this you can access many other features just inside MenuEditor class but if you find this difficult, I suggest you use Odin Inspecter. I hope you have reached your answer. comment under answer if you need more information.

Form inheritance should be abstract

Here my code
GeneralLesson.cs: it's just a .cs file, no .Designer.cs or .resx file
public /*abstract*/ class GeneralLesson : Form
{
public GeneralLesson()
{
this.StartPosition = SS.FrmStartPos;//If I want to change it later, I just change it here and all the children will get the change
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (e.CloseReason == CloseReason.WindowsShutDown) return;
SS.LearningDay = 0; //SS is my static class that hold the static variables.
SS.FrmMain.Show();
}
}
SentLesson.cs: This is actually a windows form, with .Designer.cs and .resx file
public partial class _Sent_Lesson : GeneralLesson
{
public _Sent_Lesson()
{
InitializeComponent();
richTextBox1.Text = "under construction";
}
}
So it actually serves my purpose well. my SentLesson window inherits the constructor and OnFormClosing from GeneralLesson. But the problem is I can't design my SentLesson window any more, it shows as the picture below:
Maybe I do it wrong. But I don't want to create GeneralLesson as a Window because I don't design any control on it, I just want to override some function like OnFormClosing or the Constructor. Is there any way I can do this without making GeneralLesson as a Window.
Thanks for reading.
I finally got the solution. Even though the abstract parent form still works logically, but the children form can't be designed any further with Visual Studio designer. So I have to inherit forms the Visual Studio way.
As you can see, Visual Studio provides us Inherited Form to create Children form. So I create GeneralLesson.cs as Windows Form, and SentLesson.cs as Inherited Form. So SentLesson inherits constructor and OnFormClosing from GeneralLesson and the designer of SentLesson no longer shows error.
If you want the Children form can access some controls from the Parent form, say Parent's "Button A". Just set the Parent's "Button A" 's Modifiers in properties window from Private to Protected. Then you can do anything with Button A in the Children form.
Thank for your reading.

Durandal - dispose of viewmodel after completion of registration process

Just wondering if anyone knows a good/simple approach using Durandal to disposing of or re-initializing a viewmodel once it becomes invalid?
I have a registration form that I could 're-initialize' manually after a user has completed the form and registered successfully, but I'd prefer to just dispose of it so that Durandal creates a new registraion view/view model when that particular route is accessed again.
If your viewmodel module returns a function rather than an object, it will create a new one each time rather than reusing the 'singleton' object. See the Module Values section of Creating a Module.
Updated link for the Durandal Module constructor function information: Module Values
You can split the difference:
var cache;
var ctor = function () {
if (cache) return cache;
// init logic
cache = this;
}
Just replace the if(cache) check with whatever "do I need a new thing or not" logic you like.
If you're using routing, simply redirect the user to an instance-based module (one that returns a constructor function). The user will most likely click or touch a button that signifies that he is done with the registration form. That would be the redirect action.
If you're using composition, you would still create an instance-based module. Then, you would use dynamic composition to swap it in once the user signified he was done with the registration form.
Dynamic composition is where the view and/or model attributes on a Durandal composition are, themselves, observables, referencing something like the following in the viewModel:
this.currentView = ko.observable('');
this.currentModel = ko.observable('');
Then, in your HTML:
<div>
<div data-bind="compose: {view: currentView(), model: currentModel())"></div>
</div>
When the user clicks "Done", or something to that effect, functions on your viewModel might look something like:
ctor.prototype.done = function () {
this.setCurrentView('viewmodels/registrationForm.html');
this.setCurrentModel('viewmodels/registrationForm.js');
}
ctor.prototype.setCurrentView = function (view) {
this.currentView(view);
}
ctor.prototype.setCurrentModel = function (model) {
this.currentModel(model);
}
Either one of the approaches above will create the registrationForm only when it's needed.
With Durandal 2.0, you can use the deactivate callback within the composition lifecycle. Here is some documentation http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks

ASP MVC 2 Pattern for implementing CanExecute style commands

I have come from WPF (MVVM) background and trying to shift to MVC 2. Is there any pattern in MVC2 where you can use Commanding/Command buttons like <input> which you use to submit the form so that you can hide/disable when you try to Render the View.
In MVVM world, your commands could implement ICommand interface, and it had CanExecute method which was quite useful. I was wondering if there is anything similar in ASP MVC 2 ?
The only way I can think of, is to do it in the View, so that I can check the flag on ViewModel (CanSave) and depending on that show/hide the <input> tag.
Basically I want to have 2 version of the website running, one in Read-Only mode and the other Editing mode.
Let me know if you need any clarification.
ASP.NET MVC does not feature the notion of 'controls', as are found in classic ASP.NET and WPF. The foundational blocks of ASP.NET MVC are HTML elements, like <input>, <button> et cetera. Naturally, these don't offer the functionality you're looking for (i.e. Implementation of the ICommand Interface).
The scenario that you're looking at (i.e. two modes of your form) can be (and arguably should be) dealt with at the View level. You're already facing the right direction: have a 'CanSave' property on your Model, and use this in the View to determine what is generated.
Example:
<% if (Model.CanSave)
{ %>
<p>First Name: <%= Html.TextBox("firstname", Model.firstname) %> </p>
<% }
else
{ %>
<p>First Name: <%=Model.firstname %></p>
<% } %>
You'll probably want to check out the DisplayTemplates and EditorTemplates... very handy for this scenario. Brad Wilson does a good job here.
It will help you move to this:
<%= (Model.CanSave) ? Html.EditorFor(x => x.firstname) : Html.DisplayFor(x => x.firstname) %>
...which makes your View clean and nice.
If you can't get MVC to do this it's relatively worth it to hand-code something like this vb-style pseudocode. This involves...
Subclassing your controls.
Not as much of a pain as it sounds, but, it is a medium sized one. Therefore it is only appropriate for medium-sized to large apps. But worth it for them.
Interface BaseUIControl
Property Enabled as Boolean
Property Visible as Boolean
Property Name as String
Property EntireStateAsXML as string ' You can use this to do EVERYTHING!
Interface UserActionItem
Event Clicked(sender as UserActionItem ... don't pass anything from UI namespaces!)
Class MyButton (or link, etc.) Implement BaseUIControl, UserActionItem Inherits UI.Button
How does this help? You've basically replaced the missing functionality. Your Controller (or even application layer) can be aware of the UI components by interface only, so they won't have to see the UI types.
more...
You can leverage this philosophy to control everything. This has saved me thousands of hours of monkey code.
Interface TextControl
Property Value as text
Interface CheckControl
Property Checked as boolean
The above two are Pretty basic - you inherit MyCheckBox and MyTextBox from the UI versions and implement the appropriate.
Of course you could set up common code to loop thru all controls and auto-validate (or loop thru and get each one's XML to autobind the whole form).
Interface ValidationBase
Property Required as Boolean
If you have a text or numeric-only mask or restricitons built into 2 subclasses...
Interface ValidationNumeric
Property MinVal, MaxVal as double
Interface ValidationText
Property MinLen, MaxLen as double
No, it won't go to the database for you. But this sweeps a ton of crud under the rug.
You can even set these property values in the UI designer - yes, putting BL in bed with UI, BUT, if you only have one UI for the BL, actually works very well.
Now image a UI with a mix of things like listbox/multiselect, double-list picker controls, checked listbox, a groupbox of option buttons/checkboxes ...
Interface Selector
property Items as list (of string)
property SelectedItems as list (of string)
Use what works on the UI - your generic routines can care less what they look like!! The subclassed UI pieces will just implement them to set/get the right values.
In addition ... we added 'validationEquation', ActivatesEquation (gray/ungray), SetValueTriggerEquation (if true, set value to SetValueEquation, otherwise, leave alone), which allowed controls to be set to simple values from other items (basically getting the values from bound objects as if using reflection) via Pascal Gayane's Expression Evaluator (it reads .net types!)
You can also subclass the main form, have it recurse thru all it's subcontrols, put together the XML's for the whole screen, and serialize it like that. You can have your own classes implement these in the non-UI layers and use it to totally (de/)serialize the UI state, and use them to read the UI too, if they relate to a business object, to map to it.
It's unbelievable how much this simplifies a complex app. We have one with 1200+ data entry panels (... pages... ours is a thickclient app) that will fill out 250 different paper forms at 250K LOC. The form definitions contain the 'name' of each control and this is pulled from the XML generated from the screens. We probably saved 500K LOC as many of the screens have no code behind them or only trivial code; all the databinding, validation, etc. is handled by common routines that reference the interfaces.
Like I say, this only works for a big app. Spend at least 2-3 weeks developing 90% of the functionality, though; probably another month throughout the 2 years dev maturing it. I am guessing your apps is big if you're caring about ICommand and its conveniences. I would put the payback at 15-20 moderately complex pages.
If I'm understanding the question correctly, you could write a ControllerCommand class to encapsulate this. Something like this:
public class ControllerCommand
{
public string Action { get; set; }
public string Controller { get; set; }
public object RouteValues { get; set; }
public bool IsEnabled { get; set; }
}
Your Details viewmodel might use it like this:
public class DetailsModel
{
public guid Id { get; set;}
// some other viewmodel properties
public ControllerCommand Edit { get; set; }
}
You could write extension methods on HtmlHelper to replace the built-in ones:
public MvcHtmlString CommandLink(this HtmlHelper html, string linkText, ControllerCommand command, object htmlAttributes)
{
if (command.IsEnabled)
{
return html.ActionLink(linkText, command.Action, command.Controller, command.RouteValues, htmlAttributes);
}
else
{
return MvcHtmlString.Create(linkText);
// perhaps return <span class="disabled-command">linkText</span>
}
}
One of the ways I have found is to use Filter attributes which you can put in your Actions, but that only handles CanExecute on the server side.
For the GUI side, couldnt find better way than putting If statements to check if the user is Priviliged to run particular action (i.e. Edit/Delete buttons)

Workflow Custom Activity building workflow parameters

Let's say I have a Workflow with 2 dependency Property : Prop1, Prop2.
I'd like to create a custom activity that when I drag into the workflow, It will show Prop1 and Prop2 in the property grid in the designer.
Is this possible ?
Like the invokeWorkflow, when you select the TargetWorkflow, it populates the property grid with Parameters of the workflow, so that you can bind.
You could try something like this:
http://blogs.microsoft.co.il/blogs/bursteg/archive/2006/10/29/DynamicWorkflowBindingParameters.aspx
I've been doing quite a bit of digging into dynamically creating properties during design time and I've had some success with it.
However, I haven't been able to get dynamic properties to show up in the actual property binding display. So you can create properties dynamically in the designer and set them, but you can set other properties to point to your dynamic properties.
This appears to be a limitation of the workflow designer in visual studio. I can't see a reason why the workflow engine itself can't handle this.
You shouldn't need to do anything, by default all public properties are displayed in the property grid.
If you define each one of your properties like this, the binding should be available:
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public static readonly DependencyProperty CustomParamProperty
= DependencyProperty.Register("CustomParam", typeof(int), typeof(CustomActivityClass));
public int CustomParam
{
get { return (int)GetValue(CustomParamProperty); }
set {SetValue(CustomParamProperty, value); }
}
Good Luck!